为什么 ASP.NET SPA 模板为所有请求实例化 UserManager 一次? [英] Why does the ASP.NET SPA template instantiate UserManager once for all requests?

查看:12
本文介绍了为什么 ASP.NET SPA 模板为所有请求实例化 UserManager 一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 VS2013 ASP.NET SPA 模板作为使用新 ASP.NET 标识框架的 Web 应用程序的起点.这是来自模板:

I'm using the VS2013 ASP.NET SPA template as a starting point for my web application which uses the new ASP.NET identity framework. This is from the template:

public partial class Startup
 {
     static Startup()
     {
     UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
....
}

因此,由于没有 DbContext 被传递到上面的 UserStore 构造函数,这向我表明正在创建一个新的 DbContext.由于我也想利用数据上下文(用于请求期间的其他数据操作),我稍微更改了模板代码:

So, since no DbContext is being passed into the UserStore constructor above, that indicates to me that a new DbContext is being created. Since I also wanted to make use of the data context (for other data operations during the request), I changed the template code slightly:

public partial class Startup
 {
public static DerivedDbContext=null;

    static Startup()
    {
   context = new DerivedDbContext();
           UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>(context));
...
}

现在,我可以通过访问:

Now, I can use the same DbContext from any of my controllers by accessing:

Startup.context

但是,当多个请求同时进入时,我遇到了麻烦,因为不相关的操作发生在同一个 DbContext 中.从这里开始,有人向我指出,我不应该为应用程序的整个生命周期实例化单个 DbContext,而应该仅为特定请求的生命周期实例化,所以我将实例化移动到控制器的构造函数中,但是现在,我在控制器中有自己的 DbContext,而 UserManager 仍然有自己的(并且是在 Startup 类中创建的).

But, I got into trouble when multiple requests would come in at the same time because unrelated operations were happening in the same DbContext. From here, it was pointed out to me that I should NOT be instantiating a single DbContext for the entire lifecycle of the application, but rather just for the lifecycle of a particular request, so I moved the instantiation to the controller's constructor, but now, I've got my own DbContext in the controller, and the UserManager still has its own (and is created in the Startup class).

为什么模板让 UserManager 为所有用户(在 Startup 类中)实例化一次?是否担心,通过在控制器构造函数中创建我自己的 DbContext,有两个 DbContexts(我在控制器的构造函数中创建的一个,另一个在 UserManager 中,在 Startup 类中创建)同时运行?在所有请求中共享一个 UserManager 是否可以接受,但在所有请求中共享一个 DbContext 是不可接受的?

同时使用两个独立的上下文似乎很愚蠢,我注意到有时我会获得不同步的数据视图.我想了解是否还有其他人在使用此模板时遇到过这个问题.

Having two separate contexts in play seems foolish and I'm noticing that I sometimes get an out-of-sync view of data. I'd like to understand if anyone else faced this problem with this template.

**我知道像 nInject 这样的 IOC 框架可能有助于在这种情况下管理对象生命周期,但我想首先了解如何在没有此类框架帮助的情况下实现这一目标.

** I understand that an IOC framework like nInject might help manage object lifecycles in this scenario, but I'd like to first gain an understanding of how to accomplish this without the help of such a framework.

谢谢...-本

推荐答案

所以模式应该是每个请求一个 UserManager 和一个 DbContext.在 2.0-alpha1 版本中,我们试图通过添加一些新的身份中间件来简化这两项工作:

So the pattern should be one UserManager and one DbContext per request. In the 2.0-alpha1 release, we tried to make this easier by adding some new Identity Middleware that takes care of both of these things:

我们正在开发一个更新的示例包来演示这些,但与此同时

We are working on an updated samples package that demonstrates these, but in the meantime

您可以使用新包添加以下 IdentityModels.cs/Startup.Auth.cs:

You can add the following your IdentityModels.cs/Startup.Auth.cs with the new packages:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    public static ApplicationDbContext Create() {
        return new ApplicationDbContext();
    }
}

public class ApplicationUserManager : UserManager<ApplicationUser> {
    // Configure the application user manager
    public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) {
    }

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options) {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(options.Context.GetDbContext()));
        //var manager = new ApplicationUserManager(new AzureStore<ApplicationUser>());
        manager.UserValidator = new UserValidator<ApplicationUser>(manager) {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };
        manager.PasswordValidator = new MinimumLengthValidator(6);
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null) {
            manager.PasswordResetTokens = new DataProtectorTokenProvider(dataProtectionProvider.Create("PasswordReset"));
            manager.UserConfirmationTokens = new DataProtectorTokenProvider(dataProtectionProvider.Create("ConfirmUser"));
        }
        return manager;
    }
}

public static class OwinExtensions {
    public static IAppBuilder UseDbContextFactory(this IAppBuilder app, Func<DbContext> createCallback) {
        if (app == null) {
            throw new ArgumentNullException("app");
        }
        if (createCallback == null) {
            throw new ArgumentNullException("createCallback");
        }

        app.Use(typeof(IdentityFactoryMiddleware<DbContext, IdentityFactoryOptions<DbContext>>), 
            new IdentityFactoryOptions<DbContext>() { 
                Provider = new IdentityFactoryProvider<DbContext>() {
                    OnCreate = (options) => createCallback()
                }
            });
        return app;
    }

    public static DbContext GetDbContext(this IOwinContext context) {
        if (context == null) {
            throw new ArgumentNullException("context");
        }
        return context.Get<DbContext>();
    }
}

以下内容进入 Startup.Auth.cs:

And the following goes into Startup.Auth.cs:

    public void ConfigureAuth(IAppBuilder app) {
        // Configure the db context and user manager to use per request
        app.UseDbContextFactory(ApplicationDbContext.Create);
        app.UseUserManagerFactory(new IdentityFactoryOptions<ApplicationUserManager>() {
            DataProtectionProvider = app.GetDataProtectionProvider(),
            Provider = new IdentityFactoryProvider<ApplicationUserManager>() {
                OnCreate = ApplicationUserManager.Create
            }
        });

        // Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        // Configure the sign in cookie
        app.UseCookieAuthentication(new CookieAuthenticationOptions {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromSeconds(5),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

这篇关于为什么 ASP.NET SPA 模板为所有请求实例化 UserManager 一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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