Autofac范围问题

我想让autofac工作,但我的unitofwork /用户管理器类有问题。

最初我将我的工作单元设置为每个请求实例,如下所示:

builder.RegisterType<UnitOfWork>().As().InstancePerRequest(); 

但在我的StartupConfig.cs中,我试图像这样设置oAuth:

 private static OAuthAuthorizationServerOptions ConfigureOAuthTokenGeneration(IAppBuilder app, ILifetimeScope scope) { var t = scope.Resolve(); // Get our providers var authProvider = scope.Resolve(); var refreshTokenProvider = scope.Resolve(); // Create our OAuth options return new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, // TODO: Remove this line TokenEndpointPath = new PathString("/oauth/access_token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"), Provider = authProvider, RefreshTokenProvider = refreshTokenProvider }; } 

范围是通过以下方式获得的:

 var scope = config.DependencyResolver.GetRootLifetimeScope(); 

因此,我无法将InstancePerRequest用于UnitOfWork ,而是将其更改为:

 builder.RegisterType<UnitOfWork>().As().InstancePerLifetimeScope(); 

现在应用程序实际运行,但是我的UserProvider出现了一个新错误,它实例化如下:

 builder.RegisterType().As().InstancePerRequest(); 

但如果我运行它,我会收到此错误:

从请求实例的作用域中看不到具有与“AutofacWebRequest”匹配的标记的作用域。

如果在执行Web应用程序期间看到此情况,则通常表示SingleInstance()组件(或类似方案)正在请求按HTTP请求注册的组件。 在Web集成下,始终从依赖项解析程序或请求生存期范围请求依赖项,而不是从容器本身请求。

这实际上是由行调用的:

 var authProvider = scope.Resolve(); 

在我的StartupConfig.cs中OAuthProvider确实需要UserProvider ,签名如下所示:

 public OAuthProvider(IAdvancedEncryptionStandardProvider helper, IUserProvider userProvider) { this._helper = helper; this._userProvider = userProvider; } 

因为这不在“请求”中,我改变了UserProvider ,就像这样解决:

 builder.RegisterType().As().InstancePerLifetimeScope(); 

现在匹配UnitOfWork ,项目将加载。 但是,如果我有一个尝试做两件事的接口(获取当前用户并列出所有用户),它会创建2个请求,同时创建UserController的新实例:

 public UsersController(IUserProvider provider) { this._provider = provider; } 

而这又试图创建UserProvider的 2个实例。 这会引发错误:

在创建模型时不能使用上下文。 如果在OnModelCreating方法内部使用上下文,或者同时由多个线程访问相同的上下文实例,则可能抛出此exception。 请注意,DbContext和相关类的实例成员不保证是线程安全的。

所以,我想我需要知道如何解决这个问题。 这就像我需要2个范围,一个用于应用程序的开始,然后另一个用于其他一切。 谁能帮我这个?

问题

因为,在OWIN中间件注册时,您需要提供OAuthAuthorizationServerOptions的实例,因此无法根据HTTP请求解析ProviderRefreshTokenProvider属性。

我们需要的是一种为每个HTTP请求创建OAuthAuthorizationServerOptions的方法。 通过扩展,相同的概念将适用于OAuthAuthorizationServerMiddleware

可能的解决方案

这正是AutofacMiddleware所做的; 它通过存储在OWIN上下文中的生命周期范围的HTTP请求期间解析它来包装OWIN中间件,然后执行它。 这意味着我们现在可以为每个HTTP请求实例化一个新的OAuthAuthorizationServerMiddleware

如文档中所述 ,当您在Startup类中使用app.UseAutofacMiddleware(container)时,Autofac会做两件事 :

  • 它将自己挂钩在OWIN管道中 ,为每个HTTP请求创建一个嵌套的生命周期范围
  • 它使用OwinMiddleware包装容器中注册的所有OwinMiddleware服务 ,并将它们添加到OWIN管道

然后,解决方案是在Autofac容器中注册OAuthAuthorizationServerMiddleware及其所有依赖项,并为每个请求自动解析并执行。

OAuthAuthorizationServerMiddleware有3个依赖项:

  • 管道中的下一个OWIN中间件, AutofacMiddleware将其提供给Resolve方法 – TypedParameter.From(this.Next)
  • IAppBuilder一个实例
  • OAuthAuthorizationServerOptions实例

我们必须在容器中注册最后两个依赖项和中间件本身。 我们来看看它的外观:

免责声明:我没有尝试下面的代码

 // Here go all the registrations associated with the `Provider` // and `RefreshTokenProvider` properties with the appropriate lifetimes builder .RegisterInstance(app) .As(); builder .Register(x => new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, // TODO: Remove this line TokenEndpointPath = new PathString("/oauth/access_token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"), Provider = x.Resolve(), RefreshTokenProvider = x.Resolve() }) .AsSelf() // InstancePerDependency is same as InstancePerLifetimeScope // in this case since the middleware will get resolved exactly one // time per HTTP request anyway .InstancePerDependency(); builder.RegisterType(); 

控制中间件订单

虽然这可能有效,但它可能不适合您的需求,因为OAuth中间件将在您调用app.UseAutofacMiddleware(container)的OWIN管道中注册。

如果您想要更多地控制中间件订单,可以将Autofac生命周期范围创建与OWIN管道中的中间件注册分开 :

免责声明:我没有尝试下面的代码

 // creates the per HTTP request lifetime scope app.UseAutofacLifetimeScopeInjector(container); // "usual" OWIN middlewares registrations app.UseFoo(); // now use one from the container app.UseMiddlewareFromContainer(); // other "usual" OWIN middlewares registrations app.UseBar();