14

I'm creating an API using Owin, Web API, Entity Framework, ASP.NET Identity. I'm using Simple Injector as my DI framework of choice.

During the Owin startup process, I want to seed my database with some sample data. This is handled by a class implementing IDatabaseInitializer, which looks something like this:

public class MyDbInitializer : DropCreateDatabaseAlways<MyDataContext>
{
    private readonly IUserManager _userManager;

    public MyDbInitializer(IUserManager userManager)
    {
        _userManager = userManager;
    }

    protected override void Seed(MyDataContext context)
    {
        SeedIdentities();
    }

    private void SeedIdentities()
    {
        var user = new User
        {
            UserName = "someUsername",
            Email = "some@email.com"
        };

        _userManager.CreateAsync(user, "Password");
    }

IUserManager is a proxy for the ASP.NET Identiy UserManager class, which indirectly depends on IUnitOfWork. In case you're wondering, IUserManager is registered like this:

container.Register(typeof(IUserManager), 
    () => container.GetInstance<IUserManagerFactory>().Create());

Because I want to use a single unit of work per Web API request, I have registered my IUnitOfWork as following:

container.RegisterWebApiRequest<IUnitOfWork, MyUnitOfWork>();

This is working fine and dandy for everything except when resolving the IUserManager dependency in the MyDbInitializer class. During application startup, SimpleInjector fails with the following ActivationException:

SimpleInjector.ActivationException was unhandled by user code
  HResult=-2146233088
  Message=The registered delegate for type IUserManagerFactory threw an exception. The IUnitOfWork is registered as 'Web API Request' lifestyle, but the instance is requested outside the context of a Web API Request.
   Source=SimpleInjector
   StackTrace:
     at SimpleInjector.InstanceProducer.GetInstance()
     at SimpleInjector.Container.GetInstance[TService]()
     at Project.WebApi.CompositionRoot.SimpleInjectorCompositionRoot.<RegisterSecurityDependencies>b__c() in c:\code\Project\Project.WebApi\CompositionRoot\SimpleInjectorCompositionRoot.cs:line 130
     at lambda_method(Closure )
     at lambda_method(Closure )
     at SimpleInjector.InstanceProducer.GetInstance()
   InnerException: SimpleInjector.ActivationException
     HResult=-2146233088
     Message=The IUnitOfWork is registered as 'Web API Request' lifestyle, but the instance is requested outside the context of a Web API Request.
     Source=SimpleInjector
     StackTrace:
        at SimpleInjector.Scope.GetScopelessInstance[TService,TImplementation](ScopedRegistration`2 registration)
        at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration`2 registration, Scope scope)
        at SimpleInjector.Advanced.Internal.LazyScopedRegistration`2.GetInstance(Scope scope)
        at lambda_method(Closure )
        at SimpleInjector.InstanceProducer.GetInstance()
     InnerException:

I guess that the Owin Startup.cs class is regarded as being outside the Web API request lifecycle, and that constructor injection in the database initializer fails because it is called by the Startup.cs class.

I'm unsure how to solve this problem. Should I use a hybrid lifestyle for the registration of IUnitOfWork, or is there any better solution?

Steven
  • 166,672
  • 24
  • 332
  • 435
Larrifax
  • 408
  • 3
  • 9

2 Answers2

27

You are correct. During the startup, there is no Web API request. Fortunately, the WebApiRequestLifestyle (that is used by the RegisterWebApiRequest extension method) uses the ExecutionContextLifestyle under the covers, so it is really simple to 'simulate' a web request as follows:

// using SimpleInjector.Extensions.ExecutionContextScoping;

using (container.BeginExecutionContextScope())
{
    var initializer = container.GetInstance<MyDbInitializer>();
    intializer.InitializeDatabase();
}
Steven
  • 166,672
  • 24
  • 332
  • 435
  • this would create a dependency to the IoC container. Is it somehow possible to create the execution context through SimpleInjector's configuration when requesting the MyDbInitializer object? – mcanti Jan 15 '17 at 15:09
  • @mcanti: The original question is about running this code during application startup. The start-up part of the application already has (and will always have) a dependency on your DI container. Creating an execution context scope when a scoped instance is requested is *not* advised, since it becomes unclear when to end/dispose that scope. – Steven Jan 15 '17 at 15:56
0

Also, f you're hosted in IIS, the integrated pipeline will create a new CallContext for each stage of the pipeline, which is where SimpleInjector stores the execution context.

So if you do a BeginExecutionContextScope in a piece of middleware that is in a different stage of the pipeline than your Web API code is running, the SimpleInjector execution context won't persist.

You can use app.UseStageMarker to adjust where your Middleware executes in the pipeline, but I have not been able to get the SimpleInjector execution context to persist through all of my OWIN auth middleware and then into the Web API. The following code in my DelegatingHandlerProxy always ends up creating a new execution scope despite using the same SimpleInejctor container

protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
    // Trigger the creation of the DependencyScope to ensure the instance
    // is resolved within that scope.
    request.GetDependencyScope();
}
Andy
  • 49,085
  • 60
  • 166
  • 233
BenS
  • 1