asp.net-mvc
निर्भरता अन्तःक्षेपण
खोज…
टिप्पणियों
डिपेंडेंसी इंजेक्शन (DI) का पूरा बिंदु कोड युग्मन को कम करना है। किसी भी तरह की कल्पना करें अगर बातचीत जिसमें "हार्ड कोडित निर्भरता उदाहरण" जैसे कुछ नया करना शामिल है।
लेखन कोड का एक बड़ा हिस्सा इसे परखने की क्षमता है। हर बार जब हम एक नई निर्भरता को नया करते हैं, तो हम अपने कोड को परीक्षण करना मुश्किल बनाते हैं क्योंकि हमारा उस निर्भरता पर कोई नियंत्रण नहीं होता है।
आप कोड का परीक्षण कैसे करेंगे जो उदाहरण के लिए DataTime.Now पर निर्भर करता है? यह हमेशा बदलता रहता है इसलिए आपके पास कोई संदर्भ नहीं है। यह तब होता है जब आप अपने शुरुआती बिंदु के रूप में एक स्थिर पैरामीटर इंजेक्ट करते हैं। आप इसे नियंत्रित कर सकते हैं, आप विभिन्न मूल्यों के आधार पर परीक्षण लिख सकते हैं और सुनिश्चित कर सकते हैं कि आपको हमेशा सही परिणाम मिले।
इसलिए एक अच्छा विकल्प यह है कि कंस्ट्रक्टर डि में एक पैरामीटर के रूप में एक इंटरफ़ेस या एक सार वर्ग पास किया जाए।
एक इंटरफ़ेस एक अच्छी तरह से परिभाषित अनुबंध का प्रतिनिधित्व करता है, आप हमेशा वहाँ रहने के तरीकों पर भरोसा कर सकते हैं और आप हमेशा विधि हस्ताक्षर पर भरोसा कर सकते हैं।
एक बार जब आप DI का उपयोग शुरू करते हैं तो अन्य पहलू खुल जाएंगे। उदाहरण के लिए, यहां तक कि अगर आप किसी बिंदु पर एक इंटरफ़ेस पास करते हैं, तो आपको वास्तव में किसी भी काम को करने के लिए एक वास्तविक कार्यान्वयन की आवश्यकता होगी। यह वह जगह है जहाँ अन्य अवधारणाएँ दिखाई देती हैं। हम अपनी निर्भरता को हल करने के लिए IOC (नियंत्रण का उलटा) का उपयोग कर सकते हैं। इसका मतलब है कि हम अपने कोड को हमेशा किसी भी अनुबंध के लिए एक विशिष्ट कार्यान्वयन का उपयोग करने का निर्देश देते हैं। बेशक ऐसा करने के अन्य तरीके हैं। हम हमेशा एक विशिष्ट कार्यान्वयन के साथ प्रत्येक अनुबंध को तुरंत रोक सकते हैं और उस बिंदु से हमारा कोड उस हिस्से का उपयोग कर सकते हैं:
public ILogging Logging { get; set }
कुछ बिंदु पर हम इसे शुरू करते हैं।
Logging = new FileLogging();
यह हमेशा तब तक काम करेगा जब तक हमारी कक्षा अपेक्षित अनुबंध को पूरा नहीं करती है:
public class FileLogging : ILogging
प्रारंभ के क्षण से हम हमेशा लॉगिंग ऑब्जेक्ट का उपयोग करते हैं। यह जीवन को आसान बनाता है क्योंकि यदि हम कभी भी डेटाबेसलेगिंग को बदलने और उपयोग करने का निर्णय लेते हैं, उदाहरण के लिए, हमें केवल एक स्थान पर कोड बदलना होगा और यह वही है जहां हम लॉगिंग क्लास को आरंभीकृत करते हैं।
क्या DI केवल परीक्षण के लिए अच्छा है? संधारणीय संहिता लिखने के साथ-साथ DI भी महत्वपूर्ण है। यह चिंताओं को अलग करने की अनुमति देता है।
जब आप कोई भी कोड लिखते हैं, तो सोचें ... क्या यह परीक्षण योग्य है, क्या मैं एक परीक्षण लिख सकता हूं, कि जब DateTime.Now का उपयोग करने के बजाय DateTime मान इंजेक्ट कर रहा है।
Ninject विन्यास
आईओसी (नियंत्रण का उलटा) कंटेनर की स्थापना के बाद, इसे काम करने के लिए कुछ ट्वीक्स की आवश्यकता होती है। इस मामले में, मैं Ninject का उपयोग करूँगा। NinjectWebCommon फ़ाइल में, जो App_Start फ़ोल्डर में स्थित है, CreateK कर्नेल विधि को इसके साथ प्रतिस्थापित करें:
private static IKernel CreateKernel()
{
// Create the kernel with the interface to concrete bindings
var kernel = RegisterServices();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
और RegisterServices विधि के साथ:
private static StandardKernel RegisterServices()
{
Container container = new Container();
// encapsulate the interface to concrete bindings in another class or even in another layer
StandardKernel kernel = container.GetServices();
return kernel;
}
बाइंडिंग के लिए एक नया वर्ग बनाएं जो इस स्थिति में कंटेनर कहलाता है:
public class Container
{
public StandardKernel GetServices()
{
// It is good practice to create a derived class of NinjectModule to organize the binding by concerns. In this case one for the repository, one for service and one for app service bindings
return new StandardKernel(new NinjectRepositoryModule(),
new NinjectServiceModule(),
new NinjectAppServiceModule());
}
}
अंत में प्रत्येक व्युत्पन्न NinjectModule वर्ग में लोड विधि को ओवरलोड करने वाले बाइंडिंग को संशोधित करें जैसे:
public class NinjectRepositoryModule: NinjectModule
{
public override void Load()
{
// When we need a generic IRepositoryBase<> to bind to a generic RepositoryBase<>
// The typeof keyword is used because the target method is generic
Bind(typeof (IRepositoryBase<>)).To(typeof (RepositoryBase<>));
// When we need a IUnitOfWorkbind to UnitOfWork concrete class that is a singleton
Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
}
}
व्युत्पन्न NinjectModule का एक और उदाहरण:
public class NinjectServiceModule :NinjectModule
{
public override void Load()
{
// When we need a IBenefitService to BenefitService concrete class
Bind<IBenefitService>().To<BenefitService>();
// When we need a ICategoryService to CategoryService concrete class
Bind<ICategoryService>().To<CategoryService>();
// When we need a IConditionService to ConditionService concrete class
Bind<IConditionService>().To<ConditionService>();
}
}
इंटरफेस का उपयोग
सेवा की आवश्यकता वाले ठोस वर्ग में, इसके कार्यान्वयन के बजाय सेवा तक पहुँचने के लिए इंटरफ़ेस का उपयोग करें:
public class BenefitAppService
{
private readonly IBenefitService _service;
public BenefitAppService(IBenefitService service)
{
_service = service;
}
public void Update(Benefit benefit)
{
if (benefit == null) return
_service.Update(benefit);
_service.Complete();
}
}
अब अगर आपको ठोस वर्ग में कुछ चाहिए, तो ऊपर दिए गए कोड में हस्तक्षेप नहीं करेगा। आप एक और पूरी तरह से अंतर के लिए सेवा कार्यान्वयन को बदल सकते हैं, और जब तक कि आप जाने के लिए अच्छा इंटरफ़ेस को संतुष्ट नहीं करते हैं। साथ ही इसका परीक्षण करना बहुत आसान है।
कंस्ट्रक्टर निर्भरता इंजेक्शन
कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन को निर्भरता को इंजेक्ट करने के लिए कंस्ट्रक्टर में मापदंडों की आवश्यकता होती है। इसलिए जब आप एक नई वस्तु बनाते हैं, तो आपको मूल्यों को पास करना होगा।
public class Example
{
private readonly ILogging _logging;
public Example(ILogging logging)
{
this._logging = logging;
}
}
हार्ड कोडित निर्भरता
public class Example
{
private FileLogging _logging;
public Example()
{
this._logging = new FileLogging();
}
}
पैरामीटर DI
public DateTime SomeCalculation()
{
return DateTime.Now.AddDays(3);
}
बनाम
public DateTime SomeCalculation(DateTime inputDate)
{
return inputDate.AddDays(3);
}
निन्यूज डिपेंडेंसी इंजेक्शन
डिपेंडेंसी रिज़ॉल्वर का उपयोग तंग-युग्मित वर्गों से बचने, लचीलेपन में सुधार और परीक्षण को आसान बनाने के लिए किया जाता है। आप अपने स्वयं के निर्भरता इंजेक्टर (पुन: संयोजित नहीं) बना सकते हैं या अच्छी तरह से लिखित और परीक्षण किए गए निर्भरता इंजेक्टर में से एक का उपयोग कर सकते हैं। इस उदाहरण में मैं Ninject का उपयोग करने जा रहा हूं।
एक कदम: निर्भरता रिज़ॉल्वर बनाएँ।
सबसे पहले, NuGet से Ninject डाउनलोड करें। Infrastructure नाम का फोल्डर बनाएं और NinjectD dependencyResolver नाम की क्लास लगाएं ।
using Ninject;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
public class NinjectDependencyResolver
: IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
// Initialize kernel and add bindings
kernel = new StandardKernel();
AddBindings();
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
private void AddBindings()
{
// Bindings added here
}
}
MVC फ्रेमवर्क GetService और GetServices तरीकों को कॉल करेगा जब इसे आने वाले अनुरोध को सेवा देने के लिए एक वर्ग के एक आग्रह की आवश्यकता होती है।
चरण दो: रजिस्टर निर्भरता रिज़ॉल्वर।
अब हमारे पास हमारी कस्टम निर्भरता रिज़ॉल्वर है और हमें अपनी निर्भरता रिज़ॉल्वर का उपयोग करने के लिए MVC फ्रेमवर्क को बताने के लिए इसे पंजीकृत करने की आवश्यकता है। Global.asax.cs फ़ाइल में निर्भरता रिज़ॉल्वर पंजीकृत करें:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
DependencyResolver.SetResolver(new NinjectDependencyResolver());
// .....
}
चरण तीन: बाइंडिंग जोड़ें।
कल्पना कीजिए कि हमारे पास निम्नलिखित इंटरफ़ेस और निहितार्थ हैं:
public interface ICustomCache
{
string Info { get; }
}
public class CustomCache : ICustomCache
{
public string Info
{
get
{
return "Hello from CustomCache.";
}
}
}
अगर हम CustomCache के साथ अपने कंट्रोलर को कसकर युग्मित किए बिना अपने नियंत्रक में CustomCache का उपयोग करना चाहते हैं, तो हमें ICCCache को CustomCache से बांधने और Ninject का उपयोग करके इसे इंजेक्ट करने की आवश्यकता है। सबसे पहले सबसे पहले, ICBCCache को CustomCache से जोड़कर निम्नलिखित कोड AddBindings () पद्धति के NinjectD dependencyResolver में जोड़ दें :
private void AddBindings()
{
// Bindings added here
kernel.Bind<ICustomCache>().To<CustomCache>();
}
फिर नीचे दिए गए इंजेक्शन के लिए अपना नियंत्रक तैयार करें:
public class HomeController : Controller
{
private ICustomCache CustomCache { get; set; }
public HomeController(ICustomCache customCacheParam)
{
if (customCacheParam == null)
throw new ArgumentNullException(nameof(customCacheParam));
CustomCache = customCacheParam;
}
public ActionResult Index()
{
// cacheInfo: "Hello from CustomCache."
string cacheInfo = CustomCache.Info;
return View();
}
}
यह कॉस्ट्रोक्टर इंजेक्शन का उदाहरण है और यह निर्भरता इंजेक्शन का एक रूप है । जैसा कि आप देखते हैं, हमारा होम कंट्रोलर कस्टमचैच क्लास थैलेफ पर निर्भर नहीं करता है। यदि हम अपने आवेदन में ICustomCache के एक और कार्यान्वयन का उपयोग करना चाहते हैं, तो हमें केवल एक चीज को बदलने की जरूरत है IC ICCache को दूसरे निहितार्थ के लिए बाध्य करना और वह एकमात्र कदम है जिसे हमें लेने की आवश्यकता है। यहाँ क्या हुआ, है MVC फ्रेमवर्क हमारे पंजीकृत निर्भरता समाधानकर्ता पूछा GetService विधि के माध्यम से HomeController वर्ग के उदाहरण बनाने के लिए। GetService विधि Ninject कर्नेल से अनुरोधित वस्तु बनाने के लिए कहती है और Ninject कर्नेल अपने कार्यकाल में प्रकार की जांच करता है और पता चलता है कि HomeController का निर्माण करने वाला एक ICustomCache की आवश्यकता करता है और ICustomCache के लिए बाइंडिंग पहले ही जोड़ दी गई है । निनजेक्ट, बंधे हुए वर्ग का उदाहरण बनाता है, इसका उपयोग होमकंट्रोलर बनाने के लिए करता है और इसे एमवीसी फ्रेमवर्क लौटाता है।
निर्भरता की जंजीर।
जब Ninject टाइप बनाने की कोशिश करता है, तो यह टाइप और अन्य प्रकारों के बीच अन्य अवसादों की जांच करता है और यदि कोई Ninject है तो उन्हें भी बनाने की कोशिश करता है। उदाहरण के लिए, यदि हमारे CustomCache वर्ग को ICacheKeyProvider की आवश्यकता होती है और यदि ICacheKeyProvider Ninject के लिए जोड़ा गया बाइनिंग इसे हमारी कक्षा के लिए प्रदान कर सकता है।
ICacheKeyProvider इंटरफ़ेस और SimpleCacheKeyPider साइडर अव्यवस्था :
public interface ICacheKeyProvider
{
string GenerateKey(Type type);
}
public class SimpleCacheKeyProvider
: ICacheKeyProvider
{
public string GenerateKey(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return string.Format("{0}CacheKey", type.Name);
}
}
संशोधित CustomCache वर्ग
public class CustomCache : ICustomCache
{
private ICacheKeyProvider CacheKeyProvider { get; set; }
public CustomCache(ICacheKeyProvider keyProviderParam)
{
if (keyProviderParam == null)
throw new ArgumentNullException(nameof(keyProviderParam));
CacheKeyProvider = keyProviderParam;
}
...........
}
ICacheKeyProvider के लिए बाध्यकारी जोड़ें:
private void AddBindings()
{
// Bindings added here
kernel.Bind<ICustomCache>().To<CustomCache>();
kernel.Bind<ICacheKeyProvider>().To<SimpleCacheKeyProvider>();
}
अब जब हम HomeController Ninject पर नेविगेट उदाहरण बनाता है की SimpleCacheKeyProvider इसे इस्तेमाल करता है CustomCache बनाने के लिए और HomeController बनाने के लिए CustomCache उदाहरण का उपयोग करता है।
Ninject में जंजीर निर्भरता इंजेक्शन जैसी कई शानदार विशेषताएं हैं और यदि आप Ninject का उपयोग करना चाहते हैं तो आपको उनकी जांच करनी चाहिए।