views:

437

answers:

3

I Have a custom HandleErrorAttribute that extends FilterAttribute.

How can I have Unity inject dependencies into this attribute at the same time that it injects the controller's dependencies itself?

A: 

You have two options

The first option is to write a custom ActionInvoker, which isn't nearly as hard as it sounds. Check out this blog post. It specifically deals with NInject, but Unity supports property injection so you can modify the example to use Unity.

This is the option that couples your IoC Container and isn't recommended.

public class MyFilter 
{ 
  IMyService MyService {get; set;}

  MyFilter() : MyFilter(MyUnityContainer.Resolve<IMyService>())
  { }

  MyFilter(IMyService service)
  {
    MyService = service;
  }
}
Ben Robbins
I attempted the solution found in the blog post but there seems to be some differences between Unity and Ninject, or else somewhere, someone's code is wrong :).The filters are readonly by the time they get to the overridden "InvokeExceptionFilters".So I can declare a NEW filter and inject dependencies, but if I try and inject dependencies on the provided list of ExceptionFilters, nothing happens.I could create a new filter and pass it up to the base class, but then I'd lose all declarative information provided on the attribute itself.
+1  A: 

Ok, figured it out.

Mostly I used Ben's solution above from the blog post he pointed to.

The problem is that Unity behaves a little differently.

You can't inject dependencies on the filters directly, because they are of type IActionFilter and IExceptionFilter respectively. This led me to believe they were readonly, which isn't so. It's just that Unity needs to know the explicit type in order to inject.

So, in the overridden method provided by the article, Unity users need to query the filters for the types in question, and then build them up.

public UnityActionInvoker(IUnityContainer container, IList<Type> typesToInject)
        {
            _container = container;
            _typesToInject = typesToInject;
        }

And then in the overridden method, do something like this:

  var needsInjection = filters.Where(filter => typesToInject.Contains(filter.GetType()));

A bit messy, but it only needs to be done once, and keeps everything decoupled as Ben suggests.

The other gotcha is that you can't call _container.BuildUp(filter) inside a foreach loop, because the filter is readonly in that context.

A: 

I too came across this problem and now have a working solution. It is similar to the solution described above but with some slight differences and also with the full Unity code added.

First I will be using property injection for the reason described above and, as above, I will be using the BuildUp method on Unity to inject the properties into the already created Filters.

To do this I have all of my Controllers inherit from a new custom base class. On that base class I override the CreateActionInvoker method in order to set my own custom ActionInvoker.

Protected Overrides Function CreateActionInvoker() As System.Web.Mvc.IActionInvoker
    Return CustomActionInvoker
End Function

Then in my CustomActionInvoker I override the GetFilters method.

Protected Overrides Function GetFilters(ByVal controllerContext As ControllerContext, ByVal actionDescriptor As ActionDescriptor) As FilterInfo
    Dim info = MyBase.GetFilters(controllerContext, actionDescriptor)

    For Each MyAuthorizationFilter In info.AuthorizationFilters
        MvcApplication.Container.BuildUp(MyAuthorizationFilter.GetType, MyAuthorizationFilter)
    Next

    For Each MyActionFilter In info.ActionFilters
        MvcApplication.Container.BuildUp(MyActionFilter.GetType, MyActionFilter)
    Next

    For Each MyResultFilter In info.ResultFilters
        MvcApplication.Container.BuildUp(MyResultFilter.GetType, MyResultFilter)
    Next

    For Each MyExceptionFilter In info.ExceptionFilters
        MvcApplication.Container.BuildUp(MyExceptionFilter.GetType, MyExceptionFilter)
    Next

    Return info
End Function

Contrary to what is said above I did not find that doing the buildup inside a For Each loop caused any problems. I also overcame the original problem of only having the object referenced via an interface by using one of the other overloads of the BuildUp method which takes a System.Type as well as the existing object.

With all of the above done I can now inject dependencies straight into my Filters.

Any comments and thoughts very much appreciated.

Cheers Mike

Mike Dymond