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

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

问题描述

我使用VS2013 ASP.NET SPA模板作为一个起点,我的web应用程序,它使用新的ASP.NET身份框架。这是从模板:

 公共部分类启动
 {
     静态启动()
     {
     UserManagerFactory =()=>新的UserManager&所述; IdentityUser>(新UserStore&所述; IdentityUser>());
....
}

所以,因为没有的DbContext被传递到UserStore构造以上,这表明对我正在创建一个新的DbContext。因为我也想(在请求期间的其它数据业务),以利用数据上下文,我改变了模板code稍微:

 公共部分类启动
 {
公共静态DerivedDbContext = NULL;    静态启动()
    {
   上下文=新DerivedDbContext();
           UserManagerFactory =()=>新的UserManager&所述; IdentityUser>(新UserStore&所述; IdentityUser>(上下文));
...
}

现在,我可以通过访问使用任何我控制器相同的DbContext:

  Startup.context

不过,我遇到了麻烦,当多个请求将在同一时间进来的,因为不相关的操作均在同一个的DbContext发生。从这里,有人向我指出,我不应该被实例化一个单一的DbContext应用程序的整个生命周期,而只是针对特殊需求的生命周期,让我感动的实例给控制器的构造函数,但现在,我已经得到了控制我自己的DbContext和的UserManager仍然有它自己(和启动类创建)。

为什么有模板的UserManager的所有用户(在启动类)实例化一次?它是令人关注的是,通过在控制器的构造函数创建我自己的DbContext,但在游戏中同时有两个DbContexts(一个我在控制器的构造函数创建,以及一个在的UserManager,在启动类创建)?它是可以接受的所有请求共享一个单独的UserManager,但不能接受的所有请求共享一个的DbContext带来什么影响?

在有打两个不同的情境似乎愚蠢的,我注意到,我有时会收到数据的失同步视图。我想知道,如果其他人使用该模板面临这个问题。

**编辑:据我所知,像nInject IoC框架可以帮助在这种情况下管理对象的生命周期,但我想先获得对如何做到这一点没有这样一个框架的帮助下理解。

谢谢...
-ben


解决方案

所以模式应该是的UserManager和每次请求的DbContext。在2.0 ALPHA1版本中,我们试图通过添加一些新的标识中间件都需要这些东西的照顾,使这个更简单:

我们正在努力更新的样本包,演示了这些,但在此期间

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

 公共类ApplicationUser:IdentityUser
{
    公共异步任务< ClaimsIdentity> GenerateUserIdentityAsync(的UserManager< ApplicationUser>经理)
    {
        //注意authenticationType必须CookieAuthenticationOptions.AuthenticationType定义的匹配
        VAR的UserIdentity =等待manager.CreateIdentityAsync(这一点,DefaultAuthenticationTypes.ApplicationCookie);
        //添加自定义的用户在这里声明
        返回的UserIdentity;
    }
}公共类ApplicationDbContext:IdentityDbContext< ApplicationUser>
{
    公共ApplicationDbContext()
        :基地(DefaultConnection)
    {
    }    公共静态ApplicationDbContext的Create(){
        返回新ApplicationDbContext();
    }
}公共类ApplicationUserManager:&的UserManager LT; ApplicationUser> {
    //配置应用程序用户管理器
    公共ApplicationUserManager(IUserStore< ApplicationUser>存储):基地(商店){
    }    公共静态ApplicationUserManager创建(IdentityFactoryOptions< ApplicationUserManager>选项){
        VAR经理=新ApplicationUserManager(新UserStore< ApplicationUser>(options.Context.GetDbContext()));
        // VAR经理=新ApplicationUserManager(新AzureStore< ApplicationUser>());
        manager.UserValidator =新UserValidator< ApplicationUser>(经理){
            AllowOnlyAlphanumericUserNames =假,
            RequireUniqueEmail =真
        };
        manager.PasswordValidator =新MinimumLengthValidator(6);
        VAR dataProtectionProvider = options.DataProtectionProvider;
        如果(dataProtectionProvider!= NULL){
            manager.PasswordResetTokens =新DataProtectorTokenProvider(dataProtectionProvider.Create(PasswordReset));
            manager.UserConfirmationTokens =新DataProtectorTokenProvider(dataProtectionProvider.Create(ConfirmUser));
        }
        返回经理;
    }
}公共静态类OwinExtensions {
    公共静态IAppBuilder UseDbContextFactory(这IAppBuilder应用程序,Func键和LT;的DbContext> createCallback的){
        如果(应用程序== NULL){
            抛出新的ArgumentNullException(应用程序);
        }
        如果(createCallback的== NULL){
            抛出新的ArgumentNullException(createCallback的);
        }        app.Use(typeof运算(IdentityFactoryMiddleware<的DbContext,IdentityFactoryOptions<&的DbContext GT;>)
            新IdentityFactoryOptions<&的DbContext GT;(){
                供应商=新IdentityFactoryProvider<&的DbContext GT;(){
                    在OnCreate =(期权)=> createCallback的()
                }
            });
        返回程序;
    }    公共静态的DbContext GetDbContext(此背景下IOwinContext){
        如果(上下文== NULL){
            抛出新的ArgumentNullException(上下文);
        }
        返回context.Get<&的DbContext GT;();
    }
}

和下面进入Startup.Auth.cs:

 公共无效ConfigureAuth(IAppBuilder应用程序){
        //配置数据库环境和用户管理每个请求使用
        app.UseDbContextFactory(ApplicationDbContext.Create);
        app.UseUserManagerFactory(新IdentityFactoryOptions< ApplicationUserManager>(){
            DataProtectionProvider = app.GetDataProtectionProvider(),
            供应商=新IdentityFactoryProvider< ApplicationUserManager>(){
                在OnCreate = ApplicationUserManager.Create
            }
        });        //使应用程序能够使用cookie来存储信息,在用户签订
        //并利用co​​okie来临时存储有关用户记录的信息与第三方供应商登录
        //配置在cookie中的标志
        app.UseCookieAuthentication(新CookieAuthenticationOptions {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LOGINPATH =新PathString(/帐号/登录),
            供应商=新CookieAuthenticationProvider {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity< ApplicationUserManager,ApplicationUser>(
                    validateInterval:TimeSpan.FromSeconds(5),
                    regenerateIdentity:(经理,用户)=> user.GenerateUserIdentityAsync(经理))
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

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

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

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).

Why does the template have the UserManager instantiated once for all users (in the Startup class)? Is it of concern that, by creating my own DbContext in a controller constructor, there are two DbContexts (the one I create in the controller's constructor, and the one in the UserManager, created in the Startup class) in play simultaneously? Is it acceptable to have one single UserManager shared across all requests, and yet unacceptable to share one DbContext across all requests in general?

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.

**Edit: 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.

Thanks... -Ben

解决方案

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

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

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天全站免登陆