.NET कोर DI, कंस्ट्रक्टर को पैरामीटर पास करने के तरीके


105

निम्नलिखित सेवा का निर्माण करने वाला

public class Service : IService
{
     public Service(IOtherService service1, IAnotherOne service2, string arg)
     {

     }
}

.NET कोर आईओसी तंत्र का उपयोग करके मापदंडों को पारित करने के विकल्प क्या हैं

_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( _serviceCollection.BuildServiceProvider().GetService<IOtherService>(), _serviceCollection.BuildServiceProvider().GetService<IAnotherOne >(), "" ));

क्या कोई और तरीका है ?


3
अपना डिज़ाइन बदलें। Arg को Parameter ऑब्जेक्ट में निकालें और उसे इंजेक्ट करें।
स्टीवन

जवाबों:


124

कारखाना प्रतिनिधि का अभिव्यक्ति पैरामीटर ( x इस मामले में) है IServiceProvider

का उपयोग करें कि निर्भरता को हल करने के लिए,

_serviceCollection.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));

कारखाना प्रतिनिधि एक विलंबित आह्वान है। जब कभी भी प्रकार को हल किया जाना है तो यह पूरा प्रदाता को प्रतिनिधि पैरामीटर के रूप में पारित करेगा।


1
हां, यह वह तरीका है जो मैं अभी कर रहा हूं, लेकिन क्या कोई और तरीका है? और अधिक सुंदर हो सकता है? मेरा मतलब है कि अन्य मापदंडों को पंजीकृत सेवाओं के लिए यह थोड़ा अजीब लगेगा। मैं कुछ और देख रहा हूँ जैसे कि सामान्य रूप से सेवाओं को पंजीकृत करें और केवल गैर-सेवा तर्क पास करें, इस मामले में आर्ग। Autofac की तरह कुछ करता है .WithParameter("argument", "");
बोरिस

1
नहीं, आप प्रदाता को मैन्युअल रूप से बना रहे हैं, जो खराब है। प्रतिनिधि विलंबित आह्वान है। जब कभी भी प्रकार को हल किया जाना है तो यह पूरा प्रदाता को प्रतिनिधि पैरामीटर के रूप में पारित करेगा।
नकोसी 12

@MCR जो बॉक्स से बाहर कोर DI के साथ डिफ़ॉल्ट दृष्टिकोण है।
नकोसी

12
@ नोकोसी: ActivatorUtilities.CreateInstance , Microsoft.Extensions.DependencyInjection.Abstractionsपैकेज के अपने हिस्से (ताकि कोई कंटेनर विशिष्ट निर्भरता) पर एक नज़र डालें
Tseng

धन्यवाद, @ ट्सेंग जो वास्तविक उत्तर की तरह दिखता है, हम यहां देख रहे हैं।
ब्रेनस्ल्गस83

63

यह ध्यान दिया जाना चाहिए कि अनुशंसित तरीका विकल्प पैटर्न का उपयोग करना है । लेकिन ऐसे मामलों का उपयोग किया जाता है जहां इसके अव्यावहारिक (जब पैरामीटर केवल रनटाइम पर पता होते हैं, स्टार्टअप / संकलित समय पर नहीं) या आपको गतिशील रूप से एक निर्भरता को बदलने की आवश्यकता होती है।

इसका बहुत उपयोगी है जब आपको एक एकल निर्भरता (जैसे कि यह एक स्ट्रिंग, पूर्णांक या किसी अन्य प्रकार की निर्भरता हो) को बदलने की आवश्यकता होती है या एक 3 पार्टी लाइब्रेरी का उपयोग करते समय जो केवल स्ट्रिंग / पूर्णांक मापदंडों को स्वीकार करता है और आपको रनटाइम पैरामीटर की आवश्यकता होती है।

आप एक शॉर्टकट हाथ के रूप में CreateInstance (IServiceProvider, Object []) की कोशिश कर सकते हैं (यह सुनिश्चित नहीं है कि यह स्ट्रिंग मापदंडों / मूल्य प्रकारों / आदिम (int, float, string) के साथ काम करता है, अप्रयुक्त) (बस इसे आज़माया और अपने काम की पुष्टि की, यहां तक ​​कि साथ कई स्ट्रिंग पैरामीटर) हाथ से हर एक निर्भरता को हल करने के बजाय:

_serviceCollection.AddSingleton<IService>(x => 
    ActivatorUtilities.CreateInstance<Service>(x, "");
);

पैरामीटर (के अंतिम पैरामीटर CreateInstance<T>/CreateInstance ) उन मापदंडों को परिभाषित करते हैं जिन्हें प्रतिस्थापित किया जाना चाहिए (प्रदाता से हल नहीं किया गया)। वे दिखाई देने के रूप में बाएं से दाएं लागू होते हैं (यानी पहले स्ट्रिंग को तत्काल टाइप किए जाने वाले प्रकार के पहले स्ट्रिंग-टाइप किए गए पैरामीटर के साथ बदल दिया जाएगा)।

ActivatorUtilities.CreateInstance<Service> एक सेवा को हल करने के लिए और इस एकल सक्रियण के लिए डिफ़ॉल्ट पंजीकरणों में से एक को बदलने के लिए कई स्थानों पर उपयोग किया जाता है।

उदाहरण के लिए यदि आप एक वर्ग में नामित किया है MyService, और यह है IOtherService, ILogger<MyService>निर्भरता के रूप में और आप सेवा को हल लेकिन के डिफ़ॉल्ट सेवा बदलना चाहते हैं IOtherService(अपने कहना OtherServiceA) के साथ OtherServiceB, आप की तरह कुछ कर सकता है:

myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())

तब पहले पैरामीटर को इंजेक्ट किया IOtherServiceजाएगा OtherServiceB, बल्कि इसके बजाय OtherServiceAशेष पैरामीटर कंटेनर से आएंगे।

यह तब सहायक होता है जब आपके पास बहुत अधिक निर्भरता होती है और आप केवल किसी एक का विशेष उपचार करना चाहते हैं (अर्थात अनुरोध के दौरान या किसी विशिष्ट उपयोगकर्ता के लिए कॉन्फ़िगर किए गए मान के साथ डेटाबेस विशिष्ट प्रदाता को बदलें, ऐसा कुछ जिसे आप केवल चलाते समय और अनुरोध के दौरान जानते हैं और नहीं जब आवेदन बनाया गया है / शुरू)।

आप इसके बजाय फैक्टरी विधि बनाने के लिए ActivatorUtilities.CreateFactory (प्रकार, प्रकार []) विधि का उपयोग कर सकते हैं, क्योंकि यह बेहतर प्रदर्शन GitHub संदर्भ और बेंचमार्क प्रदान करता है ।

बाद में एक उपयोगी होता है जब प्रकार को बहुत बार हल किया जाता है (जैसे कि सिग्नलआर और अन्य उच्च अनुरोध परिदृश्यों में)। मूल रूप से आप एक के ObjectFactoryमाध्यम से बनाएँगे

var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });

फिर उसे कैश करें (एक चर आदि के रूप में) और जहां जरूरत हो, उसे कॉल करें

MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);

## अपडेट: बस इसे स्ट्रिंग्स और पूर्णांक के साथ काम करने की पुष्टि करने के लिए खुद की कोशिश की, और यह वास्तव में काम करता है। यहाँ मैंने जिस ठोस उदाहरण के साथ परीक्षण किया है:

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection();
        services.AddTransient<HelloWorldService>();
        services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow"));

        var provider = services.BuildServiceProvider();

        var demoService = provider.GetRequiredService<DemoService>();

        Console.WriteLine($"Output: {demoService.HelloWorld()}");
        Console.ReadKey();
    }
}

public class DemoService
{
    private readonly HelloWorldService helloWorldService;
    private readonly string firstname;
    private readonly string lastname;

    public DemoService(HelloWorldService helloWorldService, string firstname, string lastname)
    {
        this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService));
        this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname));
        this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname));
    }

    public string HelloWorld()
    {
        return this.helloWorldService.Hello(firstName, lastName);
    }
}

public class HelloWorldService
{
    public string Hello(string name) => $"Hello {name}";
    public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}";
}

// Just a helper method to shorten code registration code
static class ServiceProviderExtensions
{
    public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class => 
        ActivatorUtilities.CreateInstance<T>(provider, parameters);
}

प्रिंटों

Output: Hello Tseng Stackoverflow

6
यह भी है कि कैसे ASP.NET कोर डिफ़ॉल्ट नियंत्रक ActivatorProvider द्वारा नियंत्रकों को तत्काल .AddControllersAsServicesबदल देता है , वे सीधे IoC से हल नहीं होते हैं (जब तक कि इसका उपयोग नहीं किया जाता है, जिसके ControllerActivatorProviderसाथServiceBasedControllerActivator
Tseng

16

यदि आप सेवा को नया करने में असहज हो गए, तो आप Parameter Objectपैटर्न का उपयोग कर सकते हैं ।

तो स्ट्रिंग पैरामीटर को अपने प्रकार में निकालें

public class ServiceArgs
{
   public string Arg1 {get; set;}
}

और कंस्ट्रक्टर अब ऐसा दिखेगा

public Service(IOtherService service1, 
               IAnotherOne service2, 
               ServiceArgs args)
{

}

और सेटअप

_serviceCollection.AddSingleton<ServiceArgs>(_ => new ServiceArgs { Arg1 = ""; });
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService, Service>();

पहला लाभ यह है कि यदि आपको सर्विस कंस्ट्रक्टर को बदलने और उसमें नई सेवाएँ जोड़ने की आवश्यकता है, तो आपको new Service(...कॉल बदलने की आवश्यकता नहीं है । एक और लाभ यह है कि सेटअप थोड़ा साफ है।

एकल पैरामीटर या दो के साथ एक निर्माता के लिए यह बहुत अधिक हो सकता है।


2
यह विकल्प पैटर्न का उपयोग करने के लिए जटिल मापदंडों के लिए अधिक सहज होगा और विकल्प पैटर्न के लिए अनुशंसित तरीका है, हालांकि केवल रनटाइम (यानी एक अनुरोध या एक दावे से पता है) के मापदंडों के लिए कम उपयुक्त है
त्सेंग

0

आप इस प्रक्रिया के साथ निर्भरता को भी इंजेक्ट कर सकते हैं

_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.