c# एएसपी.नेट एमवीसी 3 एक्शन फिल्टर में निर्भरता इंजेक्शन। इस दृष्टिकोण के साथ क्या गलत है?




asp.net-mvc asp.net-mvc-3 (3)

सेटअप यहाँ है। मान लें कि मेरे पास कुछ एक्शन फ़िल्टर है जो किसी सेवा के उदाहरण की आवश्यकता है:

public interface IMyService
{
   void DoSomething();
}

public class MyService : IMyService
{
   public void DoSomething(){}
}

मेरे पास एक एक्शनफिल्टर है जिसे उस सेवा के उदाहरण की आवश्यकता है:

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService; // <--- How do we get this injected

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

एवीसी फिल्टर में एमवीसी 1/2 इंजेक्शन निर्भरताओं में गधे में दर्द का थोड़ा सा दर्द था। सबसे आम तरीका कस्टम एक्शन इनवॉकर का उपयोग करना था जैसा कि यहां देखा जा सकता है: http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ इस कामकाज के पीछे मुख्य प्रेरणा इसलिए थी क्योंकि इस निम्नलिखित दृष्टिकोण को कंटेनर के साथ मैला और तंग युग्मन माना जाता था:

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService;

   public MyActionFilter()
      :this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container
   {

   }

   public MyActionFilter(IMyService myService)
   {
      _myService = myService;
   }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

यहां हम कन्स्ट्रक्टर इंजेक्शन का उपयोग कर रहे हैं और कंटेनर का उपयोग करने और सेवा इंजेक्ट करने के लिए कन्स्ट्रक्टर को अधिभारित कर रहे हैं। मैं सहमत हूं कि एक्शनफिल्टर के साथ कंटेनर को कसकर जोड़ता है।

मेरा सवाल यह है कि: अब एएसपी.नेट एमवीसी 3 में, जहां हमारे पास इस्तेमाल किए जाने वाले कंटेनर का एक अमूर्तता है (निर्भरता रिसेल्वर के माध्यम से) क्या ये सभी हुप्स अभी भी जरूरी हैं? मुझे प्रदर्शन करने की अनुमति दें:

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService;

   public MyActionFilter()
      :this(DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService)
   {

   }

   public MyActionFilter(IMyService myService)
   {
      _myService = myService;
   }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

अब मुझे पता है कि कुछ शुद्धवादी इस पर उपहास कर सकते हैं, लेकिन गंभीरता से, नकारात्मक क्या होगा? यह अभी भी टेस्टेबल है क्योंकि आप उस कंस्ट्रक्टर का उपयोग कर सकते हैं जो परीक्षण समय पर आईएमवाई सेवा लेता है और इस तरह एक नकली सेवा इंजेक्ट करता है। आप डीई कंटेनर के किसी भी कार्यान्वयन से बंधे नहीं हैं क्योंकि आप निर्भरता रीसोलवर का उपयोग कर रहे हैं, तो क्या इस दृष्टिकोण के लिए कोई डाउनसाइड्स हैं?

संयोग से, नए IFilterProvider इंटरफ़ेस का उपयोग करके एमवीसी 3 में ऐसा करने के लिए यहां एक और अच्छा तरीका है: http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3 निर्भरता- इंजेक्शन-in http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3


समाधान मार्क सीमैन ने सुझाव दिया कि सुरुचिपूर्ण लगता है। हालांकि एक साधारण समस्या के लिए बहुत जटिल है। AuthorizeAttribute को लागू करके ढांचे का उपयोग करना अधिक प्राकृतिक लगता है।

मेरा समाधान ग्लोबल.एक्सएक्स में पंजीकृत एक सेवा के लिए एक स्थिर प्रतिनिधि कारखाने के साथ AuthorizeAttribute बनाना था। यह किसी भी डीआई कंटेनर के लिए काम करता है और सेवा लोकेटर से थोड़ा बेहतर लगता है।

Global.asax में:

MyAuthorizeAttribute.AuthorizeServiceFactory = () => Container.Resolve<IAuthorizeService>();

मेरी कस्टम विशेषता वर्ग:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public static Func<IAuthorizeService> AuthorizeServiceFactory { get; set; } 

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return AuthorizeServiceFactory().AuthorizeCore(httpContext);
    }
}

मैं सकारात्मक नहीं हूं, लेकिन मेरा मानना ​​है कि आप केवल एक खाली कन्स्ट्रक्टर ( विशेषता भाग के लिए) का उपयोग कर सकते हैं और फिर एक कन्स्ट्रक्टर है जो वास्तव में मूल्य ( फ़िल्टर भाग के लिए) इंजेक्ट करता है। *

संपादित करें : थोड़ा पढ़ने के बाद, ऐसा प्रतीत होता है कि ऐसा करने का स्वीकार्य तरीका संपत्ति इंजेक्शन के माध्यम से है:

public class MyActionFilter : ActionFilterAttribute
{
    [Injected]
    public IMyService MyService {get;set;}

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        MyService.DoSomething();
        base.OnActionExecuting(filterContext);
    }
}

सेवा लोकेटर प्रश्न का उपयोग क्यों न करें : यह ज्यादातर आपके निर्भरता इंजेक्शन की लचीलापन को कम करता है। उदाहरण के लिए, क्या होगा यदि आप लॉगिंग सेवा इंजेक्शन दे रहे थे, और आप स्वचालित रूप से लॉगिंग सेवा को कक्षा के नाम को इंजेक्शन देने के लिए चाहते थे? यदि आप कन्स्ट्रक्टर इंजेक्शन का उपयोग करते हैं, तो यह बहुत अच्छा काम करेगा। यदि आप निर्भरता रिजॉल्वर / सेवा लोकेटर का उपयोग कर रहे हैं, तो आप भाग्य से बाहर होंगे।

अद्यतन करें

चूंकि इसे उत्तर के रूप में स्वीकार कर लिया गया है, इसलिए मैं यह कहने के लिए रिकॉर्ड पर जाना चाहता हूं कि मैं मार्क सीमन के दृष्टिकोण को प्राथमिकता देता हूं क्योंकि यह एक्शन फ़िल्टर जिम्मेदारी को विशेषता से अलग करता है। इसके अलावा, निनजेक्ट के एमवीसी 3 एक्सटेंशन में बाइंडिंग के माध्यम से एक्शन फ़िल्टर को कॉन्फ़िगर करने के कुछ बहुत शक्तिशाली तरीके हैं। अधिक जानकारी के लिए निम्नलिखित संदर्भ देखें:

अद्यतन 2

जैसा कि @usr ने नीचे दी गई टिप्पणियों में बताया है, जब कक्षा लोड हो जाती है तो एक्शनफिल्टर एट्रिब्यूट्स को तुरंत चालू किया जाता है, और वे एप्लिकेशन के पूरे जीवनकाल में रहते हैं। यदि IMyService इंटरफ़ेस को सिंगलटन नहीं माना जाता है, तो यह कैप्टिव निर्भरता होने के समाप्त होता है। यदि इसका कार्यान्वयन थ्रेड-सुरक्षित नहीं है, तो आप बहुत दर्द के लिए हो सकते हैं।

जब भी आपकी कक्षा की अपेक्षित जीवनकाल की तुलना में कम जीवनकाल के साथ निर्भरता हो, तो कारखाने को इंजेक्शन देने के बजाए उस निर्भरता पर निर्भरता उत्पन्न करने के लिए बुद्धिमान होना बुद्धिमानी है।


हां, डाउनसाइड्स हैं, क्योंकि आईडी पर निर्भरता के साथ बहुत सारे मुद्दे हैं , और उन लोगों के लिए आप Singleton सर्विस लोकेटर के साथ-साथ बास्टर्ड इंजेक्शन का उपयोग भी जोड़ सकते हैं।

फ़िल्टर का एक सामान्य वर्ग के रूप में लागू करने का एक बेहतर विकल्प है जिसमें आप जो भी सेवाएं चाहें इंजेक्ट कर सकते हैं:

public class MyActionFilter : IActionFilter
{
    private readonly IMyService myService;

    public MyActionFilter(IMyService myService)
    {
        this.myService = myService;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if(this.ApplyBehavior(filterContext))
            this.myService.DoSomething();
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if(this.ApplyBehavior(filterContext))
            this.myService.DoSomething();
    }

    private bool ApplyBehavior(ActionExecutingContext filterContext)
    {
        // Look for a marker attribute in the filterContext or use some other rule
        // to determine whether or not to apply the behavior.
    }

    private bool ApplyBehavior(ActionExecutedContext filterContext)
    {
        // Same as above
    }
}

ध्यान दें कि फ़िल्टर फ़िल्टर कॉन्टेक्स्ट की जांच कैसे करता है यह निर्धारित करने के लिए कि व्यवहार लागू किया जाना चाहिए या नहीं।

इसका मतलब यह है कि फ़िल्टर को लागू किया जाना चाहिए या नहीं, यह नियंत्रित करने के लिए आप अभी भी विशेषताओं का उपयोग कर सकते हैं:

public class MyActionFilterAttribute : Attribute { }

हालांकि, अब यह विशेषता पूरी तरह से निष्क्रिय है।

फिल्टर आवश्यक निर्भरता के साथ बनाया जा सकता है और global.asax में वैश्विक फ़िल्टर में जोड़ा जा सकता है:

GlobalFilters.Filters.Add(new MyActionFilter(new MyService()));

इस तकनीक के अधिक विस्तृत उदाहरण के लिए, हालांकि एमवीसी के बजाय एएसपी.नेट वेब एपीआई पर लागू किया गया है, यह आलेख देखें: http://blog.ploeh.dk/2014/06/13/passive-attributes





actionfilterattribute