Autofac范围问题 [英] Autofac scope issues

查看:104
本文介绍了Autofac范围问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使autofac正常运行,但是我的unitofwork/用户管理器类出现问题.

I am trying to get autofac to work, but having issues with my unitofwork / user manager classes.

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

Initially I set my unit of work up as a per request instance like this:

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

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

But in my StartupConfig.cs I was trying to set up oAuth like this:

private static OAuthAuthorizationServerOptions ConfigureOAuthTokenGeneration(IAppBuilder app, ILifetimeScope scope)
{

    var t = scope.Resolve<IPasswordHasher>();

    // Get our providers
    var authProvider = scope.Resolve<OAuthProvider>();
    var refreshTokenProvider = scope.Resolve<IAuthenticationTokenProvider>();

    // 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
    };
}

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

The scope is obtained by this:

var scope = config.DependencyResolver.GetRootLifetimeScope();

因此,我不能将 InstancePerRequest 用于 UnitOfWork ,相反,我将其更改为:

Because of this, I could not use InstancePerRequest for the UnitOfWork, instead I changed it to this:

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

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

Now the application actually runs, but I get a new error with my UserProvider, it is instantiated like this:

builder.RegisterType<UserProvider>().As<IUserProvider>().InstancePerRequest();

但是,如果我运行该命令,则会出现此错误:

But if I run that, I get this error:

从请求实例的范围中看不到带有匹配"AutofacWebRequest"标签的范围.

No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.

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

If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself.

这实际上是由以下行调用的:

This is actually being invoked by the line:

var authProvider = scope.Resolve<OAuthProvider>(); 

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

which is in my StartupConfig.cs. The OAuthProvider does need the UserProvider, the signature looks like this:

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

因此,因为这不在请求"中,所以我将 UserProvider 更改为如下解决方法:

So because this is not in the "request", I changed the UserProvider to be resolved like this:

builder.RegisterType<UserProvider>().As<IUserProvider>().InstancePerLifetimeScope();

现在与 UnitOfWork 匹配的

项目将被加载.但是,如果我有一个尝试做2件事情的界面(获取当前用户并列出所有用户),它将创建2个请求,这两个请求均会创建 UserController 的新实例:

which matches the UnitOfWork now, the project will load. But if I have an interface that tries to do 2 things (get the current user and list all users) it creates 2 requests, both creating a new instance of the UserController:

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

依次尝试创建2个 UserProvider 实例.这会引发错误:

which in turn tries to create 2 instances of the UserProvider. This throws an error:

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

The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.

所以,我想我需要知道如何解决这个问题. 就像我需要2个作用域,一个作用域用于应用程序的启动,然后另一个作用域用于其他所有功能. 有人可以帮我吗?

So, I guess I need to know how I can resolve this. It's like I need 2 scopes, one for the start of the application and then another for everything else. Can anyone help me with this?

推荐答案

问题

由于在注册OWIN中间件时,您需要提供OAuthAuthorizationServerOptions的实例,所以无法解决每个HTTP请求的ProviderRefreshTokenProvider属性.

The issue

Since, at the time of the OWIN middleware registration, you need to provide an instance of OAuthAuthorizationServerOptions, there's no way to resolve the Provider and RefreshTokenProvider properties per HTTP request.

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

What we need is a way to create the OAuthAuthorizationServerOptions per HTTP request. By extension, the same concept would apply to the OAuthAuthorizationServerMiddleware.

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

That's exactly what AutofacMiddleware<T> does; It wraps an OWIN middleware by resolving it during the HTTP request from the lifetime scope stored in the OWIN context, then executes it. This means that we can now instantiate a new OAuthAuthorizationServerMiddleware for each HTTP request.

文档中所述所述a>,当您在Startup类中使用app.UseAutofacMiddleware(container)时,Autofac会

As explained in the documentation, when you use app.UseAutofacMiddleware(container) in your Startup class, Autofac does 2 things:

  • it hooks itself in the OWIN pipeline to create a nested lifetime scope for each HTTP request
  • it wraps all the OwinMiddleware services registered in the container with AutofacMiddleware and adds them to the OWIN pipeline

然后的解决方案是将OAuthAuthorizationServerMiddleware及其所有依赖项注册到Autofac容器中,并将针对每个请求自动对其进行解析并执行.

The solution is then to register OAuthAuthorizationServerMiddleware and all its dependencies in the Autofac container, and it will be automatically resolved for each request and executed.

OAuthAuthorizationServerMiddleware具有3个依赖项:

  • 管道中的下一个OWIN中间件, AutofacMiddleware负责,因为它已将其提供给Resolve方法-TypedParameter.From(this.Next)
  • IAppBuilder
  • 的实例
  • OAuthAuthorizationServerOptions实例
  • The next OWIN middleware in the pipeline, which AutofacMiddleware takes care of as it provides it to the Resolve method - TypedParameter.From(this.Next)
  • An instance of IAppBuilder
  • The OAuthAuthorizationServerOptions instance

我们必须在容器中注册最后两个依赖项以及中间件本身.让我们看一下它的外观:

We have to register the last two dependencies plus the middleware itself in the container. Let's have a look at what this could look like:

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

// Here go all the registrations associated with the `Provider`
// and `RefreshTokenProvider` properties with the appropriate lifetimes

builder
    .RegisterInstance(app)
    .As<IAppBuilder>();

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<OAuthProvider>(),
        RefreshTokenProvider = x.Resolve<IAuthenticationTokenProvider>()
    })
    .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<OAuthAuthorizationServerMiddleware>();

控制中间件顺序

虽然这可行,但可能无法满足您的需求,因为OAuth中间件将在您调用app.UseAutofacMiddleware(container)的OWIN管道中注册.

Controlling the middleware order

While this could work, it's possible that it doesn't suit your needs since the OAuth middleware will be registered in the OWIN pipeline where you call app.UseAutofacMiddleware(container).

如果您想更好地控制中间件顺序,可以

If you want more control over middleware order, you can separate the Autofac lifetime scope creation from the middleware registration in the OWIN pipeline:

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

// creates the per HTTP request lifetime scope
app.UseAutofacLifetimeScopeInjector(container);

// "usual" OWIN middlewares registrations
app.UseFoo();

// now use one from the container
app.UseMiddlewareFromContainer<OAuthAuthorizationServerMiddleware>();

// other "usual" OWIN middlewares registrations
app.UseBar();

这篇关于Autofac范围问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆