Reputation: 17475
Using ASP.NET MVC5, EF6 & Ninject as Backend, AngularJS as frontend with token based auth (JWT).
We recently had to enable @ chars in the user names. Base on this answer in the Startup.cs (called by Ninject registration code, see below), we replaced
UserManagerFactory = () => new ApplicationUserManager(new UserStore<IdentityUser>(new SecurityDbContext()));
with
var userManager = new ApplicationUserManager(new UserStore<IdentityUser>(new SecurityDbContext()));
var validator = new UserValidator<IdentityUser>(userManager)
{
AllowOnlyAlphanumericUserNames = false
};
userManager.UserValidator = validator;
UserManagerFactory = () => userManager;
This allows the registration of user name with @ signs as wished. However, logging into the application (even with "normal" usernames), became buggy: While the first login after start of the server works as usual, any subsequent login creates the following exception:
Cannot access a disposed object.
Object name: 'ApplicationUserManager'.
Detailed error message:
Source Error: Line 18: public override async Task FindAsync(string userName, string password) Line 19: { Line 20: var result = await base.FindAsync(userName, password); Line 21: if (result == null) Line 22: { Source File: xyz\Infrastructure\ApplicationUserManager.cs Line: 20 Stack Trace: [ObjectDisposedException: Cannot access a disposed object.Object name: 'ApplicationUserManager'.] Microsoft.AspNet.Identity.UserManager`1.ThrowIfDisposed() +99 Microsoft.AspNet.Identity.d__15.MoveNext() +128 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24 xyz.Infrastructure.d__0.MoveNext() in xzy\Infrastructure\ApplicationUserManager.cs:20 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24 xyz.Infrastructure.d__0.MoveNext() in xyz\Infrastructure\ApplicationOAuthProvider.cs:39 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21 Microsoft.Owin.Security.OAuth.d__3a.MoveNext() +862 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24 Microsoft.Owin.Security.OAuth.d__1e.MoveNext() +2335 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21 Microsoft.Owin.Security.OAuth.d__0.MoveNext() +1728 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +24 Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +664 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21 Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +937 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +93 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +52 System.Runtime.CompilerServices.TaskAwaiter.GetResult() +21 Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +937 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +22 Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow() +33 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +150 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar) +42 System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +415 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
I guess it has something to do with Ninject, so here's a bit more background: the above changed code is called from the NinjectWebCommon.cs:
kernel.Bind<IUserService>()
.To<UserService>()
.WithConstructorArgument("userManager", Startup.UserManagerFactory());
Upvotes: 3
Views: 3208
Reputation: 4481
Problem: This is an object lifetime error.
Solution: Replace your code with the following.
UserManagerFactory = () =>
{
var userManager = new ApplicationUserManager(new UserStore<IdentityUser>(new SecurityDbContext()));
var validator = new UserValidator<IdentityUser>(userManager)
{
AllowOnlyAlphanumericUserNames = false
};
userManager.UserValidator = validator;
return userManager;
};
Explanation:
IUserService
, Ninject goes ahead and wants to construct a new UserService
.UserService(IUserManager userManager)
constructor cannot be called without a userManager, so Ninject calls Startup.UserManagerFactory()
, as configured.UserManagerFactory
is a lambda expression that returns the captured variable userManager
. This will always be the same instance.Dispose
is called on your user manager instance.Upvotes: 5