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 ?

2
  • What's repository? Why is uow injected if it isn't used? Commented Dec 9, 2015 at 13:23
  • Sorry, that was a typo. The repository should have been uow. I have corrected in the original code. Thank you Commented Dec 9, 2015 at 13:37

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.