ASP.NET कोर DI के साथ उदाहरणों को हल करना


302

मैं ASP.NET कोर MVC अंतर्निहित निर्भरता इंजेक्शन ढांचे का उपयोग करके मैन्युअल रूप से एक प्रकार का समाधान कैसे करूं?

कंटेनर सेट करना काफी आसान है:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

लेकिन मैं ISomeServiceइंजेक्शन लगाने के बिना कैसे हल कर सकता हूं ? उदाहरण के लिए, मैं यह करना चाहता हूं:

ISomeService service = services.Resolve<ISomeService>();

में ऐसी कोई विधियाँ नहीं हैं IServiceCollection



3
क्या आप उन्हें ConfigureServices()विधि के साथ IServiceCollectionया आवेदन में कहीं भी हल करना चाहते हैं ?
हेन्क मोललेमा

2
@ हेनकेमोलेमा: स्टार्टअप के भीतर कहीं भी वास्तव में।
डेव न्यू

जवाबों:


484

IServiceCollectionइंटरफेस के लिए प्रयोग किया जाता है के निर्माण के एक निर्भरता इंजेक्शन कंटेनर। पूरी तरह से निर्मित होने के बाद, यह एक IServiceProviderऐसे उदाहरण से बना है जिसका उपयोग आप सेवाओं को हल करने के लिए कर सकते हैं। आप IServiceProviderकिसी भी वर्ग में इंजेक्शन लगा सकते हैं । IApplicationBuilderऔर HttpContextवर्ग के रूप में अच्छी तरह से सेवा प्रदाता प्रदान कर सकते हैं, उनके माध्यम से ApplicationServicesया RequestServicesक्रमशः गुण।

IServiceProviderGetService(Type type)एक सेवा को हल करने के लिए एक विधि को परिभाषित करता है :

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

कई सुविधा विस्तार विधियाँ भी उपलब्ध हैं, जैसे serviceProvider.GetService<IFooService>()( usingfor a Microsoft.Extensions.DependencyInjection) जोड़ें ।

स्टार्टअप क्लास के अंदर सेवाओं को हल करना

निर्भरता का इंजेक्शन

क्रम को होस्ट करने वाली सेवा प्रदाता के निर्माता में कुछ सेवाओं इंजेक्षन कर सकते हैं Startupजैसे वर्ग, IConfiguration, IWebHostEnvironment( IHostingEnvironmentपूर्व -3.0 संस्करणों में), ILoggerFactoryऔर IServiceProvider। ध्यान दें कि उत्तरार्द्ध होस्टिंग परत द्वारा निर्मित एक उदाहरण है और इसमें एक आवेदन शुरू करने के लिए केवल आवश्यक सेवाएं शामिल हैं

ConfigureServices()विधि सेवाओं इंजेक्शन लगाने की अनुमति नहीं है, यह केवल एक को स्वीकार करता है IServiceCollectionतर्क। यह समझ में आता है क्योंकि ConfigureServices()आप अपने आवेदन के लिए आवश्यक सेवाओं को पंजीकृत करते हैं। हालाँकि आप स्टार्टअप के निर्माता के यहाँ इंजेक्ट की गई सेवाओं का उपयोग कर सकते हैं, उदाहरण के लिए:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

ConfigureServices()तब पंजीकृत किसी भी सेवा को Configure()विधि में अंतःक्षिप्त किया जा सकता है ; आप IApplicationBuilderपैरामीटर के बाद सेवाओं की एक मनमानी संख्या जोड़ सकते हैं :

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IFooService>();
}

public void Configure(IApplicationBuilder app, IFooService fooService)
{
    fooService.Bar();
}

मैन्युअल रूप से निर्भरता को हल करना

आप मैन्युअल रूप से संकल्प सेवाओं की जरूरत है, तो आप बेहतर उपयोग करना चाहिए ApplicationServicesप्रदान की द्वारा IApplicationBuilderमें Configure()विधि:

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

IServiceProviderअपनी Startupकक्षा के कंस्ट्रक्टर में पास और सीधे उपयोग करना संभव है , लेकिन ऊपर से इसमें सेवाओं का एक सीमित उपसमूह शामिल होगा , और इस प्रकार इसकी सीमित उपयोगिता है:

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}

यदि आपको ConfigureServices()विधि में सेवाओं का समाधान करना चाहिए , तो एक अलग दृष्टिकोण की आवश्यकता होती है। आप IServiceProviderउस IServiceCollectionउदाहरण से एक मध्यवर्ती बना सकते हैं जिसमें ऐसी सेवाएं शामिल हैं जिन्हें उस बिंदु तक पंजीकृत किया गया है :

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();

    // This will succeed.
    var fooService = sp.GetService<IFooService>();
    // This will fail (return null), as IBarService hasn't been registered yet.
    var barService = sp.GetService<IBarService>();
}

कृपया ध्यान दें: आम तौर पर आपको ConfigureServices()विधि के अंदर सेवाओं को हल करने से बचना चाहिए , क्योंकि यह वास्तव में वह जगह है जहां आप एप्लिकेशन सेवाओं को कॉन्फ़िगर कर रहे हैं । कभी-कभी आपको केवल एक IOptions<MyOptions>उदाहरण तक पहुंच की आवश्यकता होती है । आप इसे IConfigurationउदाहरण से उदाहरण के लिए MyOptions(जो अनिवार्य रूप से विकल्प रूपरेखा क्या करता है) मानों को बांधकर पूरा कर सकते हैं :

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

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


14
@HenkMollema लेकिन क्या होगा अगर मुझे कुछ भी इंजेक्ट नहीं किया जा सकता है, मेरा मतलब है कि मुझे IServiceCollectionइंजेक्शन नहीं दिया जा सकता है , कुछ वर्ग जो मैन्युअल रूप से बनाया जा रहा है ( मध्य वेयर स्कोप से बाहर ), मेरे मामले में एक अनुसूचक है, जिसे समय-समय पर कुछ सेवाओं की आवश्यकता होती है। और एक ईमेल भेजें।
मरदान गोचमुराडोव

52
चेतावनी यदि आपको सेवाओं को हल करने की आवश्यकता है ConfigureServicesऔर यह सेवा एक सिंगलटन है तो यह आपके एकल Controllerउपयोग के लिए एक अलग सिंगलटन होगा ! मुझे लगता है इस वजह से यह एक अलग का उपयोग करता है IServiceProvider- इस से बचने के लिए के माध्यम से समाधान नहीं होता है BuildServiceProviderऔर इसके बजाय से सिंगलटन के अपने देखने के लिए कदम ConfigureServicesकरने के लिए Configure(..other params, IServiceProvider serviceProvider)मेंStartup.cs
वाल

3
@ अच्छी बात है। क्योंकि यह एक अलग IServiceProviderउदाहरण है यह एक नया एकल उदाहरण बना देगा। आप सर्विस प्रोवाइडर इंस्टेंस को ConfigureServicesविधि से वापस करके इससे बच सकते हैं ताकि आपके द्वारा उपयोग किए जाने वाले कंटेनर को भी उपयोग किया जा सके।
हेनक मोलेलेमा

1
आह्वान collection.BuildServiceProvider();मुझे क्या चाहिए था, धन्यवाद!
क्रिस मैरिसिक

2
@HenkMollema आप इसे केवल एक सेवा प्रदाता के उदाहरण के साथ कैसे काम करते हैं? आमतौर पर, आप 1) अपनी कुछ निर्भरता 2 पंजीकृत करें) एक अंतरिम सेवा प्रदाता उदाहरण 3 का निर्माण करें) कुछ अन्य निर्भरताओं को पंजीकृत करने के लिए आपको जो कुछ चाहिए, उसे हल करने के लिए उस सेवा प्रदाता का उपयोग करें। बाद में, आप अंतरिम उदाहरण को वापस नहीं कर सकते, क्योंकि यह आपकी कुछ निर्भरता (3 में पंजीकृत) गायब है। क्या मैं कुछ भूल रहा हूँ?
फ़िलिप

109

मैन्युअल रूप से हल करने वाले उदाहरणों में IServiceProviderइंटरफ़ेस का उपयोग करना शामिल है:

Startup.ConfigureServices में निर्भरता का समाधान

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

स्टार्टअप में निर्भरताएँ हल करना। कॉन्फ़िगर करें

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

स्टार्टअप में निर्भरता को हल करना। ASP.NET Core 3 में कॉन्फ़िगर करें

public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    app.ApplicationServices.GetService<MyService>();
}

रनटाइम इन्जेक्टेड सेवाओं का उपयोग करना

कुछ प्रकारों को विधि मापदंडों के रूप में इंजेक्ट किया जा सकता है:

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

नियंत्रक क्रियाओं में निर्भरता का समाधान

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";

1
@AfsharMohebbi GetServiceजेनेरिक Microsoft.Extensions.DependencyInjectionनामस्थान में एक विस्तार विधि है ।
अहमदाली शफी

एक्सटेंशन के तरीकों के बारे में: एक एक्सटेंशन विधि एक स्थिर विधि है जो एक वर्ग में फंक्शनलिटी को जोड़ती है, आप सार्वजनिक स्थैतिक सिद्धांत की घोषणा कर सकते हैं .NET कोर के साथ एक बहुत ही सामान्य aproach बनें, ताकि डेवलपर्स आधार कार्यक्षमता का विस्तार कर सकें ... यहां अच्छे उदाहरण हैं: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Juan

17

यदि आप एक टेम्पलेट के साथ एक एप्लिकेशन उत्पन्न करते हैं, तो आप Startupकक्षा में ऐसा कुछ करने जा रहे हैं :

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

आप फिर वहां निर्भरता जोड़ सकते हैं, उदाहरण के लिए:

services.AddTransient<ITestService, TestService>();

यदि आप ITestServiceअपने कंट्रोलर को एक्सेस करना चाहते हैं तो आप IServiceProviderकंस्ट्रक्टर पर जोड़ सकते हैं और इसे इंजेक्ट किया जाएगा:

public HomeController(IServiceProvider serviceProvider)

फिर आप अपने द्वारा जोड़ी गई सेवा को हल कर सकते हैं:

var service = serviceProvider.GetService<ITestService>();

नोट करें कि जेनेरिक संस्करण का उपयोग करने के लिए आपको एक्सटेंशन के साथ नाम स्थान शामिल करना होगा:

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs (कॉन्फ़िगर सेवाएँ)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }

10

यदि आपको इसे पंजीकृत करने के लिए किसी अन्य निर्भरता के निर्माता को पास करने के उद्देश्य से बस एक निर्भरता को हल करने की आवश्यकता है, तो आप ऐसा कर सकते हैं।

मान लीजिए कि आपके पास एक सेवा थी जो एक स्ट्रिंग और एक ISomeService में ली गई थी।

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

जब आप इसे Startup.cs के अंदर पंजीकृत करने जाते हैं, तो आपको यह करने की आवश्यकता होगी:

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);

ओपी ने कॉन्फिगर्स सर्विस विधि में किसी सेवा को हल करने की आवश्यकता का कारण नहीं बताया, लेकिन यह सबसे अधिक संभावना है कि कोई व्यक्ति ऐसा करने के लिए
सोचेगा

1
वास्तव में यह स्वीकृत उत्तर होना चाहिए ... हालाँकि हेंक मोलेलेमा का उत्तर बहुत ही निराशाजनक है, आजकल आपका उत्तर साफ-सुथरा है और इंटरमीडिएट IServiceProvider (सिंगलेट्स के विभिन्न उदाहरण ...) के निर्माण से संबंधित समस्याओं का परिचय नहीं देता है। संभवतः, यह समाधान 2015 में उपलब्ध नहीं था, जब हेंक एवरर्ड था, लेकिन अब यह जाने का रास्ता है।
वीए 100

कोशिश की लेकिन ISomeServiceयह अभी भी मेरे लिए अशक्त था।
अंजबिएन

2 प्रश्न: 1) यदि सेवा वर्ग का पैरामीटर कंस्ट्रक्टर किसी अन्य सेवा में परिवर्तन (हटाए गए या जोड़ा गया), तो मुझे सेवा के रजिस्टर सेगमेंट IAnotherService को संशोधित करने की आवश्यकता है और यह बदलता रहता है? 2) इसके बजाय, मैं अन्य पैरामीटर के लिए केवल एक कंस्ट्रक्टर को जोड़ सकता हूं, जैसे 1 अन्य पैरामीटर के साथ पब्लिक अदर सर्विस (IServiceProvider serviceProvider) और मुझे उन सेवाओं को प्राप्त करना चाहिए जिनकी मुझे कंस्ट्रक्टर से आवश्यकता है। और मुझे बस सेवाओं की तरह स्टार्टअप क्लास में एक और सेवा सेवा रजिस्टर करने की आवश्यकता है। अतिरिक्त सामग्री <IAnotherService, एक और सेवा> (एसपी => {var सेवा = नई अन्य सेवा (सपा); वापसी सेवा;});
थॉमस.बेंज

2

आप इस तरह से AuthorizeAttribute जैसी विशेषताओं में निर्भरता को इंजेक्ट कर सकते हैं

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));

यह वही है जो मैं खोज रहा था .. धन्यवाद
रेयान चौगल

0

मुझे पता है कि यह एक पुराना सवाल है, लेकिन मैं हैरान हूं कि एक स्पष्ट और घृणित हैक यहाँ नहीं है।

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

इस पद्धति का लाभ यह है कि आपको सेवा के कॉन्फ़िगरेशन के दौरान सेवा ट्री का निर्माण करने या इसका उपयोग करने की आवश्यकता नहीं है। आप अभी भी परिभाषित कर रहे हैं कि सेवाओं को कैसे कॉन्फ़िगर किया जाएगा।

public void ConfigureServices(IServiceCollection services)
{
    //Prey this doesn't get GC'd or promote to a static class var
    string? somevalue = null;

    services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
         //create service you need
         var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
         //get the values you need
         somevalue = somevalue ?? service.MyDirtyHack();
         //return the instance
         return service;
    });
    services.AddTransient<IOtherService, OtherService>(scope => {
         //Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
         scope.GetService<IServiceINeedToUse>();
         //TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
         //Wow!
         return new OtherService(somevalue);
    });
}

इस पैटर्न को ठीक करने का तरीका OtherServiceस्पष्ट रूप से निर्भरता देना होगा IServiceINeedToUse, न कि इसके आधार पर या इसके तरीके की वापसी के मूल्य के आधार पर ... या उस निर्भरता को किसी अन्य फैशन में स्पष्ट रूप से हल करना।


-4
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}

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

किसी ने गलत तरीके से आपके जवाब को निम्न-गुणवत्ता के रूप में चिह्नित किया। आपको यह बताने के लिए कुछ पाठ जोड़ना चाहिए कि आपका उत्तर आगे बढ़ने और / या नीचे जाने से रोकने के लिए कैसे काम करता है। कोड-ओनली उत्तर कम गुणवत्ता वाला नहीं है । क्या यह प्रश्न का उत्तर देने का प्रयास करता है? यदि नहीं, तो 'उत्तर नहीं' के रूप में चिह्नित करें या विलोपन की अनुशंसा करें (यदि समीक्षा पंक्ति में है)। ख) क्या यह तकनीकी रूप से गलत है? नीचा दिखाना या टिप्पणी करना। समीक्षा से
वाई हा ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.