Why does the ASP.NET SPA template instantiate UserManager once for all requests?

匿名 (未验证) 提交于 2019-12-03 01:48:02

问题:

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

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(new UserStore(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

回答1:

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 GenerateUserIdentityAsync(UserManager 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 {     public ApplicationDbContext()         : base("DefaultConnection")     {     }      public static ApplicationDbContext Create() {         return new ApplicationDbContext();     } }  public class ApplicationUserManager : UserManager {     // Configure the application user manager     public ApplicationUserManager(IUserStore store) : base(store) {     }      public static ApplicationUserManager Create(IdentityFactoryOptions options) {         var manager = new ApplicationUserManager(new UserStore(options.Context.GetDbContext()));         //var manager = new ApplicationUserManager(new AzureStore());         manager.UserValidator = new UserValidator(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 createCallback) {         if (app == null) {             throw new ArgumentNullException("app");         }         if (createCallback == null) {             throw new ArgumentNullException("createCallback");         }          app.Use(typeof(IdentityFactoryMiddleware>),              new IdentityFactoryOptions() {                  Provider = new IdentityFactoryProvider() {                     OnCreate = (options) => createCallback()                 }             });         return app;     }      public static DbContext GetDbContext(this IOwinContext context) {         if (context == null) {             throw new ArgumentNullException("context");         }         return context.Get();     } }

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() {             DataProtectionProvider = app.GetDataProtectionProvider(),             Provider = new IdentityFactoryProvider() {                 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(                     validateInterval: TimeSpan.FromSeconds(5),                     regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))             }         });         app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!