एक नियंत्रक वर्ग के अलावा अन्य वर्गों के साथ निर्भरता इंजेक्शन


84

इस बिंदु पर, मैं अपने नियंत्रकों में चीजों को आसानी से इंजेक्ट कर रहा हूं, कुछ मामलों में अपने स्वयं के रिज़ॉल्वर सेवा वर्ग का निर्माण कर रहा हूं। जीवन अच्छा है

मुझे यह पता नहीं चल सकता है कि गैर-नियंत्रक कक्षाओं में स्वचालित रूप से इंजेक्ट करने के लिए रूपरेखा कैसे प्राप्त करें। क्या काम करता है ढांचा स्वचालित रूप से मेरे नियंत्रक में इंजेक्ट IOptionsकरता है, जो प्रभावी रूप से मेरी परियोजना के लिए कॉन्फ़िगरेशन है:

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

    public MessageCenterController(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

मैं सोच रहा हूं कि क्या मैं अपनी कक्षाओं के लिए भी ऐसा कर सकता हूं। मुझे लगता है कि जब मैं नियंत्रक की नकल करता हूं, तो मैं इस तरह से पास होता हूं:

public class MyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    }

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

मुझे लगता है कि मैं कहाँ असफल हो रहा हूँ जब मैं इसे इस तरह कहता हूं:

public void DoSomething()
{
    var helper = new MyHelper(??????);

    if (helper.CheckIt())
    {
        // Do Something
    }
}

मेरे पास इस समस्या को ट्रैक करने की समस्या व्यावहारिक रूप से सब कुछ है जो DI के बारे में नियंत्रक स्तर पर इसके बारे में बात कर रहा है। मैंने शिकार करने की कोशिश की कि यह Controllerऑब्जेक्ट स्रोत कोड में कहां होता है , लेकिन यह वहां थोड़े पागल हो जाता है।

मुझे पता है कि मैं मैन्युअल रूप से IOptions का एक उदाहरण बना सकता हूं और इसे MyHelperकंस्ट्रक्टर को पास कर सकता हूं, लेकिन ऐसा लगता है कि मुझे ऐसा करने में सक्षम होना चाहिए क्योंकि यह काम करता है Controllers


8
जब आप निर्भरता इंजेक्शन का उपयोग करते हैं तो आप कॉल नहीं करते हैं new। कभी भी उन वस्तुओं के लिए जिन्हें हल नहीं किया जाना चाहिए
त्सेंग

1
जब मैं MyHelper का एक उदाहरण बनाने की कोशिश कर रहा हूं, तो मैं नया फोन नहीं करता? (1) यह बहुत आसान लगता है, (2) यह एक वाक्यविन्यास त्रुटि है। :-)
रॉबर्ट पॉलसेन

2
हां, यह निर्भरता इंजेक्शन का पूरा बिंदु है (विशेषकर यदि नियंत्रण कंटेनरों के व्युत्क्रम का उपयोग करके जो इस तात्कालिकता का प्रबंधन और करते हैं)। अपनी सेवाओं / कक्षाओं के बाहर तात्कालिकता को उस बिंदु तक धकेलने के लिए जहां आईओसी कंटेनर आंतरिक रूप से करता है। ऐसे मामलों में जहां आप इसे निर्माता के माध्यम से इंजेक्ट नहीं कर सकते हैं आप एक कारखाना बनाते हैं और अपनी सेवा के लिए कारखाने का इंटरफ़ेस पास करते हैं। यह के कार्यान्वयन इसे सुलझाने के लिए कंटेनर का उपयोग करता है, ASP.NET कोर मामले में इंजेक्शन लगाने के IServiceProviderअपने कारखाने में और बुलाIMyHelper helper = services.RequestService<IMyHelper>()
त्सेंग

जवाबों:


42

नीचे MVC कंट्रोलर्स को शामिल किए बिना DI का उपयोग करने का एक कार्यशील उदाहरण है। यह वही है जो मुझे इस प्रक्रिया को समझने के लिए करने की आवश्यकता थी, इसलिए शायद यह किसी और की मदद करेगा।

ShoppingCart ऑब्जेक्ट, DI के माध्यम से, INotifier का एक उदाहरण मिलता है (जो उनके आदेश के ग्राहक को सूचित करता है।)

using Microsoft.Extensions.DependencyInjection;
using System;

namespace DiSample
{
    // STEP 1: Define an interface.
    /// <summary>
    /// Defines how a user is notified. 
    /// </summary>
    public interface INotifier
    {
        void Send(string from, string to, string subject, string body);
    }

    // STEP 2: Implement the interface
    /// <summary>
    /// Implementation of INotifier that notifies users by email.
    /// </summary>
    public class EmailNotifier : INotifier
    {
        public void Send(string from, string to, string subject, string body)
        {
            // TODO: Connect to something that will send an email.
        }
    }

    // STEP 3: Create a class that requires an implementation of the interface.
    public class ShoppingCart
    {
        INotifier _notifier;

        public ShoppingCart(INotifier notifier)
        {
            _notifier = notifier;
        }

        public void PlaceOrder(string customerEmail, string orderInfo)
        {
            _notifier.Send("admin@store.com", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
        }

    }

    public class Program
    {
        // STEP 4: Create console app to setup DI
        static void Main(string[] args)
        {
            // create service collection
            var serviceCollection = new ServiceCollection();

            // ConfigureServices(serviceCollection)
            serviceCollection.AddTransient<INotifier, EmailNotifier>();

            // create service provider
            var serviceProvider = serviceCollection.BuildServiceProvider();

            // This is where DI magic happens:
            var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);

            myCart.PlaceOrder("customer@home.com", "2 Widgets");

            System.Console.Write("Press any key to end.");
            System.Console.ReadLine();
        }
    }
}

17
क्या होगा यदि मैं ShoppingCartकिसी अन्य वर्ग या पद्धति में उस serviceProviderवस्तु को एक्सेस नहीं करना चाहता हूं जिसे हम एक्सेस नहीं करते हैं ?
बाघेरानी

1
यहाँ एक ही मुद्दा
केसी

4
धन्यवाद, मुझे ActivatorUtilities.CreateInstance को पाने के लिए बहुत दूर तक खोजना पड़ा।
एच। तुगकन किबर

1
क्या होगा अगर मेरे पास सेवाप्रदाता नहीं है ?? !!
HelloWorld

1
धन्यवाद! मैं इसका उपयोग Microsoft.AspNetCore.TestHost के साथ कर रहा हूं, एक TestServer बनाना और ActivatorUtilities.CreateInstance <MyCustomerController> (_ server.Host.Services) कॉल कर रहा हूं;
टोलगा

35

आइए कहते MyHelperद्वारा किया जाता है MyServiceबदले में आपके नियंत्रक द्वारा किया जाता है।

इस स्थिति को हल करने का तरीका है:

  • रजिस्टर दोनों MyServiceऔर MyHelperमें Startup.ConfigureServices

    services.AddTransient<MyService>();
    services.AddTransient<MyHelper>();
    
  • नियंत्रक को MyServiceइसके निर्माता में एक उदाहरण मिलता है ।

    public HomeController(MyService service) { ... }
    
  • MyServiceकंस्ट्रक्टर बदले में एक उदाहरण प्राप्त करेगा MyHelper

    public MyService(MyHelper helper) { ... }
    

डीआई फ्रेमवर्क समस्याओं के बिना पूरे ऑब्जेक्ट ग्राफ को हल करने में सक्षम होगा। यदि आप हर बार किसी ऑब्जेक्ट के हल होने के नए उदाहरणों के बारे में चिंतित हैं, तो आप विभिन्न जीवनकाल और पंजीकरण विकल्पों के बारे में पढ़ सकते हैं जैसे सिंगलटन या जीवनकाल का अनुरोध करें।

आपको वास्तव में संदेह होना चाहिए जब आपको लगता है कि आपको मैन्युअल रूप से कुछ सेवा का एक उदाहरण बनाना होगा, जैसा कि आप सेवा लोकेटर विरोधी पैटर्न में समाप्त हो सकते हैं । डीआई कंटेनर को ऑब्जेक्ट बनाने के लिए बेहतर छुट्टी। यदि आप वास्तव में खुद को उस स्थिति में पाते हैं (मान लें कि आप एक अमूर्त कारखाना बनाते हैं), तो आप IServiceProviderसीधे उपयोग कर सकते हैं (या तो IServiceProviderअपने निर्माता में अनुरोध करें या httpContext में उजागर एक का उपयोग करें )।

var foo = serviceProvider.GetRequiredService<MyHelper>();

मैं ASP.Net 5 DI फ्रेमवर्क और सामान्य रूप से निर्भरता इंजेक्शन के बारे में विशिष्ट दस्तावेज पढ़ने की सलाह दूंगा।


इसके साथ मुझे जो समस्या है, वह यह है कि मैं बैकग्राउंड सेवाओं का उपयोग करता हूं जो डेटाबेस के साथ इंटरैक्ट करती हैं, इसलिए dbcontext doesnt काम के साथ जीवन भर की स्कॉप्ड राइट? आप EF कोर और पृष्ठभूमि सेवाओं के साथ DI का ठीक से उपयोग कैसे करते हैं?

6

दुर्भाग्य से कोई सीधा रास्ता नहीं है। जिस तरह से मैं इसे बनाने में कामयाब रहा वह एक स्थिर वर्ग बनाकर और नीचे के रूप में हर जगह इसका उपयोग करने का है:

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

फिर अपने स्टार्टअप क्लास में, इसे नीचे दिए अनुसार भरें:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //normal as detauls , removed for space 
    // set my variables all over the site

    SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
    SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}

हालांकि यह खराब पैटर्न है, क्योंकि यह एप्लिकेशन के पूरे जीवन चक्र के लिए रहेगा और मुझे इसे बाहरी नियंत्रक के उपयोग का बेहतर तरीका नहीं मिल सकता है।


4

यहां वर्तमान .NET कोर 2.2 DI प्रलेखन के आधार पर, सीधे ओपी के प्रश्न का उत्तर देने के लिए एक और पूर्ण उदाहरण है । इस उत्तर को जोड़ने के बाद से यह .NET कोर DI के लिए नए व्यक्ति की मदद कर सकता है, और क्योंकि यह प्रश्न Google के शीर्ष खोज परिणाम है।

सबसे पहले, MyHelper के लिए एक इंटरफ़ेस जोड़ें:

public interface IMyHelper
{
    bool CheckIt();
}

दूसरा, इंटरफ़ेस को लागू करने के लिए MyHelper वर्ग को अपडेट करें (इंटरफ़ेस स्टूडियो में, इंटरफ़ेस लागू करने के लिए ctrl- दबाएँ।)

public class MyHelper : IMyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    {

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

तीसरा, डीआई सेवा कंटेनर में एक फ्रेमवर्क प्रदान की गई सेवा के रूप में इंटरफ़ेस को पंजीकृत करें। Startup.cs में ConfigureServices विधि में कंक्रीट प्रकार MyHelper के साथ IMyHelper सेवा को पंजीकृत करके ऐसा करें।

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<IMyHelper, MyHelper>();
    ...
}

चौथा, सेवा के एक उदाहरण को संदर्भित करने के लिए एक निजी चर बनाएं। कंस्ट्रक्टर में एक तर्क के रूप में सेवा पास करें (कंस्ट्रक्टर इंजेक्शन के माध्यम से) फिर सेवा उदाहरण के साथ चर को इनिशियलाइज़ करें। निजी चर के माध्यम से कस्टम वर्ग के इस उदाहरण पर किसी भी गुण या कॉल विधियों का संदर्भ दें।

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;
    private readonly IMyHelper _myHelper;

    public MessageCenterController(
        IOptions<MyOptions> options,
        IMyHelper myHelper
    )
    {
        _options = options.value;
        _myHelper = myHelper;
    }

    public void DoSomething()
    {
        if (_myHelper.CheckIt())
        {
            // Do Something
        }
    }
}

0

आप Activator.CreateInstance () का उपयोग कर सकते हैं। यहाँ इसके लिए एक आवरण समारोह है। इसका उपयोग करने का तरीका इस प्रकार है।

var determinedProgrammatically = "My.NameSpace.DemoClass1"; // implements IDemo interface
var obj = CreateInstance<My.NameSpace.IDemo, string>(determinedProgrammatically, "This goes into the parameter of the constructor.", "Omit this parameter if your class lives in the current assembly");

अब आपके पास obj का एक उदाहरण है जो प्रोग्रामेटिक रूप से निर्धारित प्रकार से त्वरित है। इस ओब्जेक्ट को नॉन कंट्रोलर क्लासेस में इंजेक्ट किया जा सकता है।

public TInterface CreateInstance<TInterface, TParameter>(string typeName, TParameter constructorParam, string dllName = null)
{
    var type = dllName == null ? System.Type.GetType(typeName) :
            System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.StartsWith(dllName, System.StringComparison.OrdinalIgnoreCase)).GetType(typeName);
    return (TInterface)System.Activator.CreateInstance(type, constructorParam);

}

पुनश्च: आप अपने वर्ग के सदन का नाम निर्धारित करने के लिए System.AppDomain.CurrentDomain.GetAssemblies () के माध्यम से पुनरावृति कर सकते हैं। यह नाम रैपर फ़ंक्शन के 3 पैरामीटर में उपयोग किया जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.