Search code examples
c#asp.net-web-api2simple-injector

Simple Injector - How to inject a property with type Func<T>


I am using a code sample (which uses StructureMap for DI) that has a property in a class as follows:

public Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

Following Simple Injector tutorial, I ended up with the following code which will supposedly do the property injection:

public class DependencyAttributeSelectionBehaviour : IPropertySelectionBehavior
{
    public bool SelectProperty(Type type, PropertyInfo prop)
    {
        return prop.GetCustomAttributes(typeof(Dependency)).Any();
    }
}

public class Dependency: Attribute
{
       // dummy class
}

Inside Global.asax

 var container = new Container();
 container.Options.PropertySelectionBehavior = new DependencyAttributeSelectionBehaviour();

then I go ahead and decorate the property as:

[Dependency]
public Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

However I still get a null exception when UnitOfWorkFactory() is called.

Dependencies and a global attribute are initialized as follows in Global.asax:

    // Create the container as usual.
    var container = new Container();
    container.Options.PropertySelectionBehavior = new DependencyAttributeSelectionBehaviour();

    // Register your types, for instance using the RegisterWebApiRequest
    // extension from the integration package:
    container.RegisterWebApiRequest<IUnitOfWork, NHibernateUnitOfWork>();
    container.RegisterSingle<ISessionSource, NHibernateSessionSource>();
    container.RegisterSingle<IAppSettings, AppSettings>();

    container.Register(() =>
    {
        var uow = (INHibernateUnitOfWork) (container.GetInstance<IUnitOfWork>());
        return uow.Session;
    });

    container.Register<IUserRepository, UserRepository>();

    // This is an extension method from the integration package.
    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    container.Verify();

    GlobalConfiguration.Configuration.DependencyResolver =
        new SimpleInjectorWebApiDependencyResolver(container);

    GlobalConfiguration.Configuration.Filters.Add(new UnitOfWorkAttribute());

This makes sense since property injection will not magically define a body for my function property but I don't have any idea how else to achieve this. Any ideas/tips are appreciated.

EDIT:

    public class UnitOfWorkAttribute : ActionFilterAttribute
{
    [Dependency]
    private Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        UnitOfWorkFactory().Begin();
    }

    // TODO: Fix this: We should also probably check if exception is handled
    public override void OnActionExecuted(HttpActionExecutedContext filterContext)
    {
        var uow = UnitOfWorkFactory();

        try
        {
            if (filterContext.Exception != null)
            {
                uow.Rollback();
            }
            else
            {
                uow.Commit();
            }
        }
        catch
        {
            uow.Rollback();
            throw;
        }
        finally
        {
            uow.Dispose();
        }
    }
}

Solution

  • Your question is starting to make sense. You are trying to inject a factory into a property of an Web API filter attribute. Attributes are always created by the CLR; not by your DI container. That's why the dependency wasn't injected. You probably already know that Web API caches filter attributes indefinitely, making them effectively singletons and this means that you shouldn't inject anything but singletons into them. That explains why you are injecting a factory; the factory can be a singleton.

    To get this to work, you need to do two things. First of all you need to register the Func<IUnitOfWork> as singleton as follows:

    container.RegisterSingle<Func<IUnitOfWork>>(container.GetInstance<IUnitOfWork>);
    

    The second thing you need is to create a custom IFilterProvider that allows injecting properties into attributes.

    As a matter of fact, the Web API integration library for Simple Injector contains an extension method that registers a custom filter provider into the Web API pipeline that does this.

    Although this extension method might sound like the answer; it isn't. It isn't the answer, because this extension method will be marked [Obsolete] in the next minor release (2.8) and it will be removed from the library in the next major release (3.0). The reason for this is that it is really easy to accidentally inject a dependency into an attribute that isn't a singleton and this can cause all kinds of nasty concurrency issues. But since attributes can't be created by the container, Simple Injector is blind and is unable to you about this error using the Diagnostic Services.

    For that reason we decided to remove this feature in a future release. The alternatives are explained in the Web API integration guide wiki page. Basically the documentation says that you should either fall back to the Service Locator pattern (by calling DependencyResolver.Current.GetService) or make your attributes passive, as explained here.