在Owin启动期间,简单注入器无法注入每个Web API请求注册的类

本文关键字:Web API 请求 注册 注入 启动 Owin 注入器 简单 | 更新日期: 2023-09-27 18:18:45

我正在使用Owin, Web API,实体框架,ASP创建一个API。净的身份。我选择简单注入器作为我的DI框架。

在Owin启动过程中,我想用一些示例数据播种我的数据库。这是由实现IDatabaseInitializer的类处理的,它看起来像这样:

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是ASP的代理。NET标识UserManager类,间接依赖于IUnitOfWork。如果你想知道,IUserManager是这样注册的:

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

因为我想使用每个Web API请求的单个工作单元,我已经注册了我的IUnitOfWork如下:

container.RegisterWebApiRequest<IUnitOfWork, MyUnitOfWork>();

除了在MyDbInitializer类中解析IUserManager依赖之外,这一切都工作得很好。在应用程序启动期间,SimpleInjector失败,并产生以下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:

我猜Owin Startup.cs类被认为是在Web API请求生命周期之外,并且数据库初始化器中的构造函数注入失败,因为它是由Startup.cs类调用的。

我不知道如何解决这个问题。我是否应该使用混合生活方式注册IUnitOfWork,或者有更好的解决方案?

在Owin启动期间,简单注入器无法注入每个Web API请求注册的类

你是正确的。在启动期间,没有Web API请求。幸运的是,WebApiRequestLifestyle(由RegisterWebApiRequest扩展方法使用)在掩护下使用ExecutionContextLifestyle,因此"模拟"web请求非常简单,如下所示:

// using SimpleInjector.Extensions.ExecutionContextScoping;
using (container.BeginExecutionContextScope())
{
    var initializer = container.GetInstance<MyDbInitializer>();
    intializer.InitializeDatabase();
}

另外,如果你托管在IIS中,集成的管道将为管道的每个阶段创建一个新的CallContext,这是SimpleInjector存储执行上下文的地方。

因此,如果你在一个中间件中执行BeginExecutionContextScope,而这个中间件与你的Web API代码运行的管道处于不同的阶段,那么SimpleInjector的执行上下文将不会持续存在。

您可以使用app.UseStageMarker来调整中间件在管道中的执行位置,但我无法让SimpleInjector执行上下文持续存在于所有我的OWIN认证中间件中,然后进入Web API。下面的代码在我的DelegatingHandlerProxy中总是最终创建一个新的执行范围,尽管使用了相同的SimpleInejctor容器

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();
}