हमारे अनुप्रयोगों के अंदर निर्भरता इंजेक्शन को प्रबंधित करने के कई तरीके हैं, और मुझे लगता है कि विभिन्न तरीकों के लाभों और सीमाओं को समझना महत्वपूर्ण है। सुझाए गए AutofacServiceProviderFactory
का उपयोग करने के बजाय ASP.NET Core में Autofac ContainerBuilder
का उपयोग करके अपनी निर्भरता को संरचित करने का प्राथमिक तरीका एक ऐसा तरीका है जिसे हम तलाश सकते हैं!
इस लेख में, मैं आपके ASP.NET कोर एप्लिकेशन में AutofacServiceProviderFactory के बजाय Autofac के ContainerBuilder का उपयोग करने के तरीके पर प्रकाश डालता हूँ। हम देखेंगे कि आप इस दृष्टिकोण के साथ क्या कर सकते हैं और क्या नहीं कर सकते हैं, जबकि निर्भरता इंजेक्शन के साथ हमारे पास अन्य दृष्टिकोण उपलब्ध हैं।
यह एक श्रृंखला का हिस्सा होगा जहाँ मैं ASP.NET Core के अंदर Autofac के साथ निर्भरता समाधान का पता लगाऊँगा। मैं नीचे दी गई श्रृंखला को शामिल करना सुनिश्चित करूँगा क्योंकि मुद्दे प्रकाशित होते हैं:
इस श्रृंखला के अंत में, आप ASP.NET Core और Blazor के अंदर प्लगइन आर्किटेक्चर का अधिक आत्मविश्वास से पता लगाने में सक्षम होंगे, जो आपके लिए अन्वेषण करने के लिए और भी अधिक सामग्री प्रदान करेगा।
निष्पक्षता से कहें तो, अनुभाग का शीर्षक *लगभग* क्लिक-बेट है। मुझे लगता है कि आपके ASP.NET कोर अनुप्रयोगों में Autofac को सेट करने के लिए सुझाए गए तरीके के रूप में AutofacServiceProviderFactory
उपयोग किया जाना अधिकांश अनुप्रयोगों के लिए बहुत अच्छा है। अधिकांश डेवलपर्स जो Autofac को अपनी पसंद के निर्भरता इंजेक्शन फ्रेमवर्क के रूप में उपयोग करना चाहते हैं, उन्हें इस तरह से बहुत अधिक समस्याओं का सामना नहीं करना पड़ेगा।
यह हमें ये क्षमता प्रदान करता है:
WebApplicationBuilder
(और इस समय उपलब्ध किसी भी चीज़) तक पहुँचेंIConfiguration
के इंस्टैंस तक पहुंच ( WebApplicationBuilder
इंस्टैंस से भी उपलब्ध)
लेकिन मेरे लिए सबसे बड़ी समस्या यह है: हम WebApplication
इंस्टेंस तक नहीं पहुँच सकते। जब मैं C# में प्लगइन आर्किटेक्चर बनाता हूँ, खास तौर पर ASP.NET कोर एप्लिकेशन बनाते समय, मैं रूट रजिस्टर करने के लिए WebApplication
इंस्टेंस तक पहुँचना पसंद करता हूँ। इससे मैं अपने प्लगइन से कम से कम API को आसानी से रजिस्टर कर सकता हूँ, जिसके लिए तकनीकी रूप से केवल IEndpointRouteBuilder
के कार्यान्वयन तक पहुँच की आवश्यकता होती है ताकि आसान सिंटैक्स प्राप्त किया जा सके।
क्या मैं इसके बिना गैर-न्यूनतम API पंजीकृत कर सकता हूँ? बिल्कुल। क्या समान सिंटैक्स प्रदान करने और WebApplication
इंस्टेंस की आवश्यकता न होने का कोई और तरीका है? बहुत संभव है। लेकिन उस समस्या को हल करने की कोशिश करने के बजाय, मैं यह देखना चाहता था कि क्या मैं उस निर्भरता तक पहुँच प्राप्त कर सकता हूँ जिसमें मेरी रुचि है।
अब समय आ गया है कि मैं अपनी निर्भरता कंटेनर को स्थापित करने की योजना को बदलूं!
आइए एक नमूना एप्लीकेशन देखें ताकि हमारे पास खोजने के लिए कुछ सामान्य आधार हो। यदि आपने पिछला लेख पढ़ा है, तो यह समान दिखाई देगा - नमूना मौसम एप्लीकेशन का एक रूपांतर:
using Autofac; using Microsoft.AspNetCore.Mvc; // personal opinion: // I absolutely love having the entry point of my // applications being essentially: // - make my dependencies // - give me the primary dependency // - use it // - ... nothing else :) ContainerBuilder containerBuilder = new(); containerBuilder.RegisterModule<MyModule>(); using var container = containerBuilder.Build(); using var scope = container.BeginLifetimeScope(); var app = scope.Resolve<WebApplication>(); app.Run(); internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } internal sealed class MyModule : Module { protected override void Load(ContainerBuilder containerBuilder) { containerBuilder.RegisterType<DependencyA>().SingleInstance(); containerBuilder.RegisterType<DependencyB>().SingleInstance(); containerBuilder.RegisterType<DependencyC>().SingleInstance(); containerBuilder .Register(ctx => { var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs()); return builder; }) .SingleInstance(); containerBuilder .Register(ctx => ctx.Resolve<WebApplicationBuilder>().Configuration) .As<IConfiguration>() .SingleInstance(); containerBuilder .Register(ctx => { var builder = ctx.Resolve<WebApplicationBuilder>(); var app = builder.Build(); app.UseHttpsRedirection(); // FIXME: the problem is that the Autofac ContainerBuilder // was used to put all of these pieces together, // but we never told the web stack to use Autofac as the // service provider. // this means that the minimal API will never be able to // find services off the container. we would need to resolve // them BEFORE the API is called, like in this registration // method itself, from the context that is passed in. //DependencyA dependencyA = ctx.Resolve<DependencyA>(); // FIXME: But... What happens if something wants to take a // dependency on the WebApplication instance itself? Once the // web application has been built, there's no more adding // dependencies to it! var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet( "/weatherforecast", ( [FromServices] DependencyA dependencyA , [FromServices] DependencyB dependencyB , [FromServices] DependencyC dependencyC ) => { var forecast = Enumerable .Range(1, 5) .Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }); return app; }) .SingleInstance(); } } internal sealed class DependencyA( WebApplicationBuilder _webApplicationBuilder); internal sealed class DependencyB( WebApplication _webApplication); internal sealed class DependencyC( IConfiguration _configuration);
इस दृष्टिकोण का एक आह्वान यह है कि हमारे प्राथमिक निर्भरता कंटेनर के रूप में ऑटोफैक ContainerBuilder
वर्ग का उपयोग करने से हमें अपने प्रवेश बिंदु को संरचित करने का अवसर मिलता है:
मेरी राय में, यह आदर्श एप्लिकेशन स्टार्टअप कोड है। क्यों? क्योंकि आपको इसे छूने के लिए कभी भी यहाँ वापस आने की ज़रूरत नहीं है। कभी नहीं। चाहे आप इसमें कितनी भी चीज़ें जोड़ लें! और यह सब इसलिए है क्योंकि आप ज़्यादा मॉड्यूल लोड करने के लिए असेंबली को स्कैन कर सकते हैं।
पुनः, यह मेरी व्यक्तिगत पसंद है, और मैं यह दावा नहीं कर रहा हूं कि यह हर किसी का लक्ष्य होना चाहिए।
बेशक, एक और तरीका जो पूरी तरह से बुलेटप्रूफ नहीं है। तो आइए चर्चा करते हैं कि ऑटोफ़ैक के इस सेटअप से हमें क्या नहीं मिलता है:
WebApplication
बनाया गया था, वह निर्भरता इंजेक्शन फ्रेमवर्क के रूप में ऑटोफ़ैक का उपयोग करने के लिए कॉन्फ़िगर नहीं किया गया है!WebApplication
इंस्टेंस प्राप्त नहीं कर सकते हैं... इसलिए हमने उस तक पहुँचने के लिए कोई विशेष प्रगति नहीं की है।
लेकिन बस इतना ही! यह कमियों की एक भयानक सूची नहीं है, लेकिन ऑटोफ़ैक कंटेनरबिल्डर दृष्टिकोण हमारे लिए एक चांदी की गोली समाधान नहीं था। तो, हमें इससे क्या मिला? निम्नलिखित को समझाने में भी मदद करेगा:
हम जो कुछ भी करते हैं उसके पक्ष और विपक्ष! अब जब हमने ASP.NET Core में Autofac ContainerBuilder के साथ समस्याओं को देखा है, तो यह देखने का समय है कि हमें इससे क्या प्रगति मिली है:
WebApplicationBuilder
और IConfiguration
इंस्टैंस तक पहुंच सकते हैं, इसलिए यह AutofacServiceProviderFactory
दृष्टिकोण का उपयोग करने के लिए एक तुलनीय लाभ है।हमने देखा कि हम ऑटोफ़ैक पंजीकरण विधि के भीतर न्यूनतम API पंजीकृत कर सकते हैं, लेकिन दुर्भाग्य से, हम सीधे न्यूनतम API कॉल पर कंटेनर से निर्भरता को हल नहीं कर सकते। हम निम्नलिखित की तरह एक समर्पित वर्ग बना सकते हैं जो निर्भरता को स्वचालित रूप से हल करने के साथ रूट परिभाषाओं को संभालता है:
internal sealed class WeatherForecastRoutes( DependencyA _dependencyA // FIXME: still can't depend on this because // we can't get the WebApplication //, DependencyB _dependencyB , DependencyC _dependencyC) { private static readonly string[] _summaries = new[] { "Freezing", "Bracing", // ... }; public WeatherForecast[] Forecast() { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), _summaries[Random.Shared.Next(_summaries.Length)] )) .ToArray(); return forecast; } }
अगर हमारे पास यह क्लास और निर्भरताएँ सभी एक ही कंटेनर पर हैं, तो स्वचालित समाधान होता है। फिर यह केवल इस कोड को कॉल करने का मामला है:
var weatherForecastRoutes = ctx.Resolve<WeatherForecastRoutes>(); app.MapGet("/weatherforecast2", weatherForecastRoutes.Forecast);
प्लगइन के नजरिए से यह अभी भी थोड़ा निराशाजनक है, क्योंकि हमें पंजीकरण कोड को कॉल करने के लिए सभी रूट क्लासों को मैन्युअल रूप से हल करना होगा - यह सब इस तथ्य से उपजा है कि ये चीजें WebApplication
इंस्टेंस तक अपनी पहुंच को हल नहीं कर सकती हैं।
लेकिन रुकिए... क्या होगा अगर हम चीजों को पलट दें? क्या होगा अगर हम IRegisterRoutes
जैसे कुछ इंटरफ़ेस को हल कर सकें और WebApplication
इंस्टेंस में पास कर सकें?!
// NOTE: make sure to register WeatherForecastRouteRegistrar on the // autofac container as IRegisterRoutes! internal interface IRegisterRoutes { void RegisterRoutes(WebApplication app); } internal sealed class WeatherForecastRouteRegistrar( WeatherForecastRoutes _weatherForecastRoutes) : IRegisterRoutes { public void RegisterRoutes(WebApplication app) { app.MapGet("/weatherforecast2", _weatherForecastRoutes.Forecast); } } // TODO: add this to the autofac code where the // WebApplication instance is built: foreach (var registrar in ctx.Resolve<IEnumerable<IRegisterRoutes>>()) { registrar.RegisterRoutes(app); }
अब, हमें इस बात की परवाह करने की भी ज़रूरत नहीं है कि WebApplication
इंस्टेंस हमारे प्लगइन्स के लिए सुलभ है या नहीं! हो सकता है कि पहला संस्करण इतना सीमित न रहा हो। शायद हम यहाँ कुछ कर रहे हैं... हालाँकि, अगले लेख में इसे और विस्तार से समझाया जाना चाहिए।
इस लेख में, मैंने AutofacServiceProviderFactory
का उपयोग करने के बजाय स्पष्ट रूप से Autofac ContainerBuilder
का उपयोग करने का पता लगाया, जैसा कि आमतौर पर सुझाया जाता है। हमने कुछ समान लाभ और कमियाँ देखीं, लेकिन विचार करने के लिए अलग-अलग चीज़ें भी थीं। प्रत्येक तरीका आपके आवेदन में आपकी ज़रूरत के हिसाब से फ़ायदे और नुकसान दे सकता है।
दिलचस्प बात यह थी कि अगर हम प्लगइन्स की दिशा में काम करने की कोशिश कर रहे हैं, तो हमें अपने प्लगइन्स से WebApplication
इंस्टेंस तक पहुँचने की ज़रूरत ही नहीं पड़ेगी! अगर हम न्यूनतम API की परवाह करते हैं, तो यह अभी भी सीमित हो सकता है... लेकिन अन्यथा, हम एक दिलचस्प सोच पर हैं!
यदि आपको यह उपयोगी लगा और आप अधिक सीखने के अवसरों की तलाश कर रहे हैं, तो मेरे मुफ्त साप्ताहिक सॉफ्टवेयर इंजीनियरिंग न्यूज़लेटर की सदस्यता लेने पर विचार करें और यूट्यूब पर मेरे मुफ्त वीडियो देखें!