2015-12-09 11 views
2

Я следил за этим article и получил все, кроме зависимостей инъекции (частично). В моем проекте я использую единство, и я пытаюсь создать настраиваемый атрибут транзакции, целью которого является запуск транзакции NHibernate до выполнения действия и фиксация/откат транзакции после выполнения метода.Пользовательский фильтр действия единичная зависимость инъекция web api 2

Это определение моего атрибута: -

public class TransactionAttribute : Attribute 
{ 
} 

Ниже приводится определение моего TransactionFilter

public class TransactionFilter : IActionFilter 
{ 
    private readonly IUnitOfWork _unitOfWork; 

    public TransactionFilter(IUnitOfWork uow) { 
     _unitOfWork = uow; 
    } 

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) { 
     var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault(); 
     if (transAttribute == null) { 
     return continuation(); 
     } 

     var transaction = uow.BeginTransaction(); 
     return continuation().ContinueWith(t => 
     { 
     try{ 
      transaction.Commit(); 
      return t.Result; 
     } 
     catch(Exception e) 
     { 
      transaction.Rollback(); 
      return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result; 
     } 
     } 
    } 
} 

И я создал пользовательский поставщик фильтр, который использует единство, чтобы построить этот фильтр.

public class UnityActionFilterProvider 
    : ActionDescriptorFilterProvider, 
     IFilterProvider 
    { 
     private readonly IUnityContainer container; 

     public UnityActionFilterProvider(IUnityContainer container) 
     { 
      this.container = container; 
     } 

     public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) 
     { 
      foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>()) 
      { 
       // TODO: Determine correct FilterScope 
       yield return new FilterInfo(actionFilter, FilterScope.Global); 
      } 
     } 
    } 

я зарегистрировать UnityActionFilterProvider в UnityWebApiActivator (я использую пакет Unity.AspNet.WebApi) следующим образом

public static void Start() 
     { 
      var container = UnityConfig.GetConfiguredContainer(); 
      var resolver = new UnityDependencyResolver(container); 
      var config = GlobalConfiguration.Configuration; 
      config.DependencyResolver = resolver; 

      var providers = config.Services.GetFilterProviders(); 
      var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider); 
      config.Services.Remove(typeof(IFilterProvider), defaultProvider); 
      config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container)); 
     } 

Проблема в том, что все работает нормально для первого запроса о каких-либо действий, но последующие запросы то же действие не воссоздает TransactionFilter, что означает, что он не вызывает конструктор для назначения нового UOW. Я не думаю, что могу отключить кэширование фильтра действий.

Единственный вариант, который я получил сейчас, это использовать шаблон локатора службы и получить экземпляр UOW с использованием контейнера внутри ExecuteActionFilterAsync, который, на мой взгляд, убивает цель этого, и мне лучше реализовать пользовательский ActionFilterAttribute.

Любые предложения?

+0

Что такое 'repository'? Почему вводится «uow», если он не используется? –

+0

К сожалению, это была опечатка. Репозиторий должен был быть uow. Я исправил исходный код. Спасибо – tangokhi

ответ

1

Насколько я могу говорить в течение многих лет, то, что происходит в стартовом коде веб-приложения, по существу, имеет время жизни Синглтона. Этот код работает только один раз.

Это означает, что есть только один экземпляр каждого из ваших фильтров. Это хорошо для производительности, но не соответствует вашему сценарию.

Самое простое решение этой проблемы, хотя немного вытекающей абстракции, чтобы впрыснуть в Abstract Factory вместо самой зависимости:

public class TransactionFilter : IActionFilter 
{ 
    private readonly IFactory<IUnitOfWork> _unitOfWorkFactory; 

    public TransactionFilter(IFactory<IUnitOfWork> uowFactory) { 
     _unitOfWorkFactory = uowFactory; 
    } 

    // etc... 

Затем использовать завод в методе ExecuteActionFilterAsync:

var transaction = _unitOfWorkFactory.Create().BeginTransaction(); 

Более элегантное решение, на мой взгляд, должно было бы использовать Decoraptor, который адаптирует TransactionFilter, но вышеупомянутый ответ, вероятно, легче понять.

+0

Спасибо. Это хорошее предложение. – tangokhi