تزریق وابستگی‌ها در فیلترهای ASP.NET MVC
فرض کنید فیلتر سفارشی لاگ کردن را که از سرویس ILogActionService استفاده می‌کند، به نحو ذیل تعریف کرده‌اید:
public interface ILogActionService
{
    void Log(string data);
}

public class LogAttribute : ActionFilterAttribute
{
    public ILogActionService LogActionService { get; set; }
 
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        LogActionService.Log("......data......");
        base.OnActionExecuted(filterContext);
    }
}
با استفاده‌ای مانند:
 [Log]
public ActionResult Index()
{}

روش متداول تنظیمات تزریق وابستگی‌ها در ASP.NET MVC، بیشتر به بحث کنترلرها مرتبط است و سایر قسمت‌ها را پوشش نمی‌دهد. برای این مورد خاص ابتدا نیاز است یک FilterProvider سفارشی را به نحو ذیل تدارک دید:
using StructureMap;
using System.Collections.Generic;
using System.Web.Mvc;
 
namespace DI06.CustomFilters
{
    public class StructureMapFilterProvider : FilterAttributeFilterProvider
    {
        private readonly IContainer _container;
        public StructureMapFilterProvider(IContainer container)
        {
            _container = container;
        }
 
        public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            var filters = base.GetFilters(controllerContext, actionDescriptor);
            foreach (var filter in filters)
            {
                _container.BuildUp(filter.Instance);
                yield return filter;
            }
        }
    }
}
نکته‌ی مهم آن، استفاده از متد BuildUp استراکچرمپ است. نمونه‌ی آن‌را در تنظیمات تزریق وابستگی‌ها در وب فرم‌ها پیشتر ملاحظه کرده‌اید. در این مثال کار آن وهله سازی وابستگی‌های فیلترهای تعریف شده در برنامه است.
پس از اینکه FilterProvider سفارشی مخصوص کار با استراکچرمپ را تهیه کردیم، اکنون نوبت به جایگزین کردن آن با FilterProvider پیش فرض ASP.NET MVC در فایل global.asax.cs به نحو ذیل است:
 //Using the custom StructureMapFilterProvider
var filterProvider = FilterProviders.Providers.Single(provider => provider is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(filterProvider);
FilterProviders.Providers.Add(SmObjectFactory.Container.GetInstance<StructureMapFilterProvider>());
استفاده از SmObjectFactory.Container.GetInstance سبب خواهد شد تا به صورت خودکار، وابستگی تزریق شده‌ی در سازنده‌ی کلاس StructureMapFilterProvider وهله سازی و تامین شود.
همچنین در این مثال چون تزریق وابستگی در کلاس LogAttribute از نوع setter injection است، نیاز است در تنظیمات ابتدایی Container مورد استفاده، Policies.SetAllProperties نیز قید شود:
namespace DI06.IocConfig
{
    public static class SmObjectFactory
    {
        private static readonly Lazy<Container> _containerBuilder =
            new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
 
        public static IContainer Container
        {
            get { return _containerBuilder.Value; }
        }
 
        private static Container defaultContainer()
        {
            return new Container(x =>
            {
                x.For<ILogActionService>().Use<LogActionService>();
 
                x.Policies.SetAllProperties(y =>
                {
                    y.OfType<ILogActionService>();
                });
            });
        }
    }
}


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
DI06
  • #
    ‫۹ سال و ۱ ماه قبل، شنبه ۱۴ شهریور ۱۳۹۴، ساعت ۱۷:۰۵
    با تشکر
    در مورد GlobalFilter راه حلی هست؟
    • #
      ‫۹ سال و ۱ ماه قبل، شنبه ۱۴ شهریور ۱۳۹۴، ساعت ۱۷:۱۱
      به چه مشکلی برخوردید؟
        • #
          ‫۹ سال و ۱ ماه قبل، شنبه ۱۴ شهریور ۱۳۹۴، ساعت ۱۹:۲۳
          در مورد فیلترهای سراسری، حلقه‌ی زیر در کلاس StructureMapFilterProvider فراخوانی نخواهد شد:
          var filters = base.GetFilters(controllerContext, actionDescriptor);
          foreach (var filter in filters)
          بنابراین container.BuildUp ایی هم بر روی فیلتر در حال اجرا، فراخوانی نمی‌شود و وابستگی‌های آن تامین نخواهند شد.
          برای حل این مشکل، بجای روش معمول معرفی فیلترهای سراسری:
           GlobalFilters.Filters.Add(new LogAttribute());
          بنویسید:
           GlobalFilters.Filters.Add(SmObjectFactory.Container.GetInstance<LogAttribute>());
          در این حالت، در ابتدای کار برنامه، تمام وابستگی‌های مرتبط با LogAttribute هم وهله سازی می‌شوند.

          مشکل!
          این وهله سازی، فقط یکبار آن هم در ابتدای برنامه انجام می‌شود. یعنی وابستگی‌های استفاده شده‌ی در فیلتر سراسری، صرفنظر از طول عمر تعریف شده‌ی برای آن‌ها توسط IoC Container، دیگر وهله سازی مجدد نخواهند شد و این مساله برای حالت‌هایی مانند کار با دیتابیس مشکل ساز است.
          برای حل این مشکل، اینترفیس IContainer را به فیلتر تزریق کنید:
          public class LogAttribute : ActionFilterAttribute
          {
              private readonly IContainer _container;
           
              //نباید به این صورت تعریف شود چون در فیلترهای سراسری فقط یکبار وهله سازی خواهد شد
              //public ILogActionService LogActionService { get; set; }
           
              public LogAttribute(IContainer container)
              {
                  _container = container;
              }
           
              public override void OnActionExecuted(ActionExecutedContext filterContext)
              {
                  _container.GetInstance<ILogActionService>().Log("......data......");
                  //LogActionService.Log("......data......");
                  base.OnActionExecuted(filterContext);
              }
          }
          در این حالت هرچند کلاس LogAttribute ایی که به صورت فیلتر سراسری تعریف شده‌است، یکبار در آغاز کار برنامه وهله سازی می‌شود، اما وابستگی‌های مورد نیاز آن، توسط container.GetInstance به ازای هر بار فراخوانی، مجددا ساخته خواهند شد و دیگر تک وهله‌ای نخواهند بود.

          روش بهتر transient کردن وابستگی‌ها: استفاده از Func
        • #
          ‫۹ سال و ۱ ماه قبل، شنبه ۱۴ شهریور ۱۳۹۴، ساعت ۲۰:۵۲
          با تشکر از زحمات فراوان شما، راه حل دیگر :
          public class StructureMapGlobalFilterProvider : IFilterProvider
              {
                  public StructureMapGlobalFilterProvider(IContainer container, GlobalFilterRegistrationList filterList)
                  {
                      _container = container;
                      _filterList = filterList;
                  }
          
                  private IContainer _container;
                  private GlobalFilterRegistrationList _filterList;
          
                  public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
                  {
                      var filters = new List<Filter>();
                      if (_filterList == null || _filterList.Count == 0)
                          return filters;
                      foreach (GlobalFilterRegistration registration in _filterList)
                      {
                          var actionFilter = _container.GetInstance(registration.Type);
                          var filter = new Filter(actionFilter, FilterScope.Global, registration.Order);
                          filters.Add(filter);
                      }
                      return filters;
                  }
              }
          
              public class GlobalFilterRegistration
              {
                  public Type Type { get; set; }
                  public int? Order { get; set; }
              }
          
              public class GlobalFilterRegistrationList : List<GlobalFilterRegistration>
              {
              }

          و تنظیمات Global:
           var globalFilterRegistrationList = new GlobalFilterRegistrationList
                          {
                              new GlobalFilterRegistration
                              {
                                  Type = typeof (LogAttribute),
                                  Order
                                      = 1
                              }
                          };
                          container.Configure(x =>
                          {
                              x.For<IFilterProvider>().Use<StructureMapGlobalFilterProvider>();
                              x.For<GlobalFilterRegistrationList>().Use(globalFilterRegistrationList);
                          });
  • #
    ‫۷ سال و ۵ ماه قبل، سه‌شنبه ۱۵ فروردین ۱۳۹۶، ساعت ۰۶:۰۰
    ضمن تشکر از شما، در صورت استفاده از Attribute ساخته شده، لازم است تا پارامتری از نوع IContainer به آن معرفی شود. در فایل ضمیمه، این Attribute کامنت شده است که با Un-comment کردن آن، پیغام خطای زیر را می‌دهد:
    Constructor 'LogAttribute' has 1 parameter(s) but invoked with 0 argument(s)
    لطفا بفرمایید برای برطرف کردن این مشکل چه باید کرد؟
      • #
        ‫۷ سال و ۵ ماه قبل، سه‌شنبه ۱۵ فروردین ۱۳۹۶، ساعت ۰۶:۱۷
        ضمن تشکر ویژه از توجه شما، بله، از فایلهای همین مخزن استفاده کردم. در اینجا هم همان Attribute کامنت شده است. با حذف آن، مجددا همان پیام را دریافت می‌کنم. مگر مطابق با این قطعه کد:
                public LogAttribute(IContainer container)
                {
                    _container = container;
                }
        استفاده از این Attribute نیازمند پارامتر ورودی IContainer نیست؟
        لطف می‌کنید راهنمایی بفرمایید.
        • #
          ‫۷ سال و ۵ ماه قبل، سه‌شنبه ۱۵ فروردین ۱۳۹۶، ساعت ۰۶:۳۶
          - این فیلتر به صورت عمومی و سراسری تعریف شده‌است. یعنی به متد مدنظر هم اعمال می‌شود و تمام اکشن متدهای دیگر.
          - خطای یاد شده، مرتبط است به کامپایلر زبان سی شارپ و ربطی به این بحث ندارد. این نوع محدودیت‌ها در ASP.NET Core برطرف شده‌اند (قسمت « تزریق وابستگی‌ها در فیلترها »ی آن).
          • #
            ‫۷ سال و ۵ ماه قبل، سه‌شنبه ۱۵ فروردین ۱۳۹۶، ساعت ۰۶:۵۳
            بله. فهمیدم. الان کامل متوجه شدم. سپاس.
            درصورتیکه - بنا به هر دلیلی - نیاز نباشد این فیلتر به صورت سراسری اعمال شود، آیا روش در پیش گفته نیاز به تغییر دارد؟ مشخصا منظور همان اعمال تزریق وابستگی‌ست. 
            • #
              ‫۷ سال و ۵ ماه قبل، سه‌شنبه ۱۵ فروردین ۱۳۹۶، ساعت ۰۶:۵۵
              - در MVC 5 این نوع وابستگی‌های فیلترها را به صورت خواص عمومی Func تعریف کنید (بهترین راه حل ممکن برای آن؛ تا حالت Transient بجای Singleton پیدا کنند). یک مثال
              Public Func<IUsersService> UsersService {set;get;}
              - در ASP.NET Core نیازی به این روش نیست و تزریق وابستگی مستقیم در سازنده‌ی فیلترها پشتیبانی رسمی می‌شود: فیلترها در ASP.NET Core (قسمت «تزریق وابستگی‌ها در فیلترها»ی آن). 
  • #
    ‫۵ سال و ۷ ماه قبل، دوشنبه ۸ بهمن ۱۳۹۷، ساعت ۱۴:۰۲
    سلام در صورتی که از  Ninject استفاده کنیم به چه صورت خواهد بود؟