2016-10-01 3 views
2

Я все еще немного новичок в использовании контейнеров IOC, и я немного борюсь. Я использую ASP.NET MVC 5.2 с Ninject.MVC3. У меня есть фильтр исключения, в основном руки прочь к службе журнала:Как сделать инъекцию зависимостей внутри ASP.NET MVC's RegisterGlobalFilters метод

public class ExceptionLoggerFilter : IExceptionFilter 
{ 
    private readonly ILogService _logService; 

    public ExceptionLoggerFilter(ILogService logService) { 
     _logService = logService; 
    } 

    public void OnException(ExceptionContext filterContext) { 
     _logService.LogError(filterContext.Exception); 
    } 
} 

Я хотел бы использовать инъекции зависимостей, чтобы получить экземпляр службы журнала. Тем не менее, метод RegisterGlobalFilters статична:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new ExceptionLoggerFilter(???)); 
    } 
} 

Я мог бы использовать статический конструктор для FilterConfig, но это просто не чувствует себя как хорошее решение.

Я попытался с помощью инъекции свойства в классе ExceptionLoggerFilter, но это просто не работает (LogService всегда нуль):

[Inject] 
public ILogService LogService { get; set; } 

Я прочитал на использование шаблона Factory, но это не делает похоже, помогут здесь, так как мне все равно придется вводить класс Factory в статический конструктор класса FilterConfig. Я могу ошибаться, но это тоже не похоже на правильное решение. Метод

В настоящем время, я использую только что дать-мне-ан-примера:

var logService = (ILogService)DependencyResolver.Current.GetService(typeof(ILogService)); 
filters.Add(new ExceptionLoggerFilter(logService)); 

Это работает, но это хорошее решение? Есть ли способ лучше? Является ли мое мышление об инъекции зависимостей и контейнерах МОК испорченными?

ответ

0

Вы должны сначала зарегистрироваться ExceptionLoggerFilter типа, как IExceptionFilter.
А потом добавить:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     var logger = DependencyResolver.Current.GetService<IExceptionFilter>(); 
     filters.Add(logger); 
    } 
} 
+2

Выполнение этого, как правило, плохая идея, если вы точно не знаете, что регистрация 'IExceptionFilter' (и всех ее зависимостей) ar e singleletons, поскольку глобальный фильтр MVC хранится глобально, что означает, что они становятся одиночными. – Steven

3

MVC глобальные фильтры просто список экземпляров. Это означает, что любые зависимости такого фильтра также становятся одиночными. Это означает, что вы должны быть очень осторожны с этим подходом, потому что действительно легко случайно вызвать Captive Dependency в вашем приложении. Captive Dependencies часто трудно отслеживать, а часто только всплывающие окна при тестировании или производстве.

Вместо этого вы должны создать прокси-класс, который может делегировать обратно в ваш контейнер/ядро ​​во время использования фильтра, чтобы он мог разрешить реальный фильтр на месте. Это предотвращает Captive Dependencies.

Такой прокси-сервер может выглядеть следующим образом:

public class NinjectExceptionFilterProxy<TExceptionFilter> : IExceptionFilter 
    where TExceptionFilter : IExceptionFilter 
{ 
    private readonly Kernel _kernel; 

    public ExceptionLoggerFilter(Kernel kernel) { 
     _kernel = kernel; 
    } 

    public void OnException(ExceptionContext filterContext) { 
     var filter = _kernel.Get<TExceptionFilter>(); 
     filter.OnException(filterContext); 
    } 
} 

Вы можете зарегистрировать прокси-фильтр следующим образом:

public static void RegisterGlobalFilters(GlobalFilterCollection filters, IKernel kernel) 
{ 
    filters.Add(new NinjectExceptionFilterProxy<ExceptionLoggerFilter>(kernel)); 
} 
0

Вы можете сделать это путем создания пользовательского поставщика фильтра, который позволит вам перехватывать процесс создания ваших фильтров, и на этом этапе вы можете ввести свои зависимости.

Ниже приведен пример с Unity, вы можете легко преобразовать его в Ninject

Создать класс для поставщика фильтра

public class UnityActionFilterProvider : FilterAttributeFilterProvider 
{ 
    private readonly IUnityContainer container; 

    public UnityActionFilterProvider(IUnityContainer container) 
    { 
     this.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.GetType(), filter.Instance); 
     } 

     return filters; 
    } 
} 

Далее в FilterConfig.cs, обновите RegisterGlobalFilters использовать новый фильтр провайдер

public class FilterConfig 
    { 
     public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
     { 
      var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider); 
      FilterProviders.Providers.Remove(oldProvider); 

      var container = UnityConfig.GetConfiguredContainer(); 
      var provider = new UnityActionFilterProvider(container); 
      FilterProviders.Providers.Add(provider); 

      filters.Add(new HandleErrorAttribute()); 
     } 
    }