1

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();
        }
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
Cemre Mengü
  • 18,062
  • 27
  • 111
  • 169
  • I tried to reproduce this, but I didn't succeed. If I use your code the container throws " Property of type Func with name 'UnitOfWorkFactory' can't be injected, because no registration for type Func could be found." when I try to resolve the object containing that property. Can you update your question and show a complete working example of your problem? – Steven Mar 02 '15 at 13:47
  • @Steven So you caught me :) So here we go then I provided more details what else do you need ? All I am trying to is initialize that property with an instance but nothing I tried has worked so I am out of ideas.. – Cemre Mengü Mar 04 '15 at 15:58
  • Try this `container.Register>(() => container.GetInstance(), "UnitOfWorkFactory");` – Aron Mar 04 '15 at 16:16
  • @Aron I tried: `container.Register>(() => container.GetInstance(), "UnitOfWorkFactory");` I got: `Cannot implicitly convert type 'TipsBackend.Models.UnitOfWork.IUnitOfWork' to 'System.Func'` – Cemre Mengü Mar 04 '15 at 16:17
  • @Cemre I don't believe you when you say you explicitly typed the generic Register method. – Aron Mar 04 '15 at 16:23
  • @Aron I don't understand what you mean ? – Cemre Mengü Mar 04 '15 at 16:25
  • I believe you used `container.Register(() => container.GetInstance(), "UnitOfWorkFactory");` which would resolve to `container.Register(() => container.GetInstance(), "UnitOfWorkFactory");` – Aron Mar 04 '15 at 16:32

1 Answers1

4

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.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • It is because the example is that way but I am open to any suggestions it does not have to be that way. This returns an error saying `cannot implicitly convert type 'TipsBackend.Models.UnitOfWork.IUnitOfWork' to 'System.Func'` – Cemre Mengü Mar 04 '15 at 16:21
  • Instead I tried this `container.RegisterWebApiRequest(container.GetInstance);` but I still get null error – Cemre Mengü Mar 04 '15 at 16:24
  • @Cemre: Please update your question and be as specific as possible. Who is giving you the error? The compiler? What is the exact error message of the compiler? Do you get an exception at runtime? What is the exact exception message, exception type and stack trace? Please also show the class that contains this `Func` property. – Steven Mar 04 '15 at 16:27
  • I updated the question showing the class. So when I run the program and try to make an api call such as api/values the program tries to start a unit of work but it fails since the property of that class (`UnitOfWorkFactory `) is null. The error is a `null reference exception` – Cemre Mengü Mar 04 '15 at 16:30
  • @Cemre: Ahh.. okay, I saw your update. You are trying to inject into a property of an ATTRIBUTE! That completely changes the story. Let me update the question... – Steven Mar 04 '15 at 16:32