自定义动作过滤器统一依赖注入web api 2

时间:2015-12-09 11:34:44

标签: nhibernate asp.net-web-api2 custom-action-filter

我遵循了这个article并且除了依赖注入(部分)之外一切正常。在我的项目中,我正在使用unity,我正在尝试创建一个自定义Transaction属性,其目的是在执行操作之前启动NHibernate事务,并在方法执行后提交/回滚事务。

这是我的属性的定义: -

this.svgContainer.append('rect')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', '100%')
        .attr('height', this.maxHeight)
        .attr('clip-path', `url(#${this.options.selectionAreaClipId})`)
        .classed('selectionArea', true);

以下是我的TransactionFilter的定义

public class TransactionAttribute : Attribute
{
}

我创建了一个自定义过滤器提供程序,它使用unity来构造此过滤器。

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;
         }
      }
   }
}

我在UnityWebApiActivator中注册了UnityActionFilterProvider(我使用的是Unity.AspNet.WebApi包),如下所示

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);
            }
        }
    }

问题是,对于任何操作的第一个请求,一切正常,但后续对同一操作的请求不会重新创建TransactionFilter,这意味着它不会调用构造函数来分配新的UOW。我不认为我可以禁用动作过滤器缓存。

我现在唯一的选择是使用服务定位器模式并使用ExecuteActionFilterAsync中的容器获取UOW实例,在我看来,这样做会破坏我的目的,我最好实现自定义ActionFilterAttribute。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

就我多年来的情况而言,Web应用程序启动代码中发生的事情基本上具有Singleton生命周期。该代码只运行一次。

这意味着每个过滤器只有一个实例。这对性能有好处,但不适合您的场景。

这个问题的最简单的解决方案,虽然有点漏洞,但是注入抽象工厂而不是依赖本身:

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

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

   // etc...

然后使用ExecuteActionFilterAsync方法中的工厂:

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

在我看来,更优雅的解决方案是使用适应TransactionFilter的{​​{3}},但上述答案可能更容易理解。