Search code examples
nhibernateasp.net-web-api2custom-action-filter

Custom action filter unity dependency injection web api 2


I followed this article and got everything working except dependency inject (partially). In my project I am using unity and I am trying to create a custom Transaction attribute the purpose of which is to start a NHibernate transaction before the execution of an action and commit/rollback the transaction after the method execution.

This is the definition of my attribute:-

public class TransactionAttribute : Attribute
{
}

Following is the definition of my 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;
         }
      }
   }
}

And I have created a custom filter provider which uses unity to construct this filter.

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

I register the UnityActionFilterProvider in UnityWebApiActivator (I am using Unity.AspNet.WebApi package) as follows

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

The problem is everything works ok for the first request for any action but subsequent requests for the same action doesn't recreate the TransactionFilter which means it doesn't call the constructor to assign a new UOW. I don't think I can disable the action filter caching.

The only option I have got now is to use the service locator pattern and get UOW instance using container inside ExecuteActionFilterAsync which in my opinion kills the purpose of this and I am better off implementing custom ActionFilterAttribute.

Any suggestions ?


Solution

  • As far as I've been able to tell during the years, what happens in web application startup code essentially has Singleton lifetime. That code only runs once.

    This means that there's only a single instance of each of your filters. This is good for performance, but doesn't fit your scenario.

    The easiest solution to that problem, although a bit of a leaky abstraction, is to inject an Abstract Factory instead of the dependency itself:

    public class TransactionFilter : IActionFilter 
    {
       private readonly IFactory<IUnitOfWork> _unitOfWorkFactory;
    
       public TransactionFilter(IFactory<IUnitOfWork> uowFactory) {
          _unitOfWorkFactory = uowFactory;
       }
    
       // etc...
    

    Then use the factory in the ExecuteActionFilterAsync method:

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

    A more elegant solution, in my opinion, would be to use a Decoraptor that Adapts the TransactionFilter, but the above answer is probably easier to understand.