Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core Identity. Using ApplicationDbContext and UserManager. Do they share the context?

i have an database initialization method in an ASP.NET MVC Core 2 application. In that method i'm using ApplicationDbContext and UserManager to initialize the database. I get both instances from the constructor's dependency injection:

public static void Initialize(ApplicationDbContext context,
            UserManager<ApplicationUser> user)

What i want to know is if they share the same context instance or if one context is created for ApplicationDbContext and another for UserManager. If i execute something like this:

ApplicationUser adminTeacherUser = new ApplicationUser
            {
                UserName = "[email protected]",
                Email = "[email protected]",
                EmailConfirmed = true,
                Name = "test",
                LastName = "test",
                BirthDate = null,
                EntryDate = DateTime.Now                    
            };

            userManager.CreateAsync(adminTeacherUser, "password").Wait();

The user is created at database inmediatly after the CreateAsync call.

But, if then i update the user like this:

adminTeacherUser.Name = "other";                
userManager.UpdateAsync(adminTeacherUser);

After the call to UpdateAsync nothing is updated at the database. The name of the user stills being "test".

But, if then i execute:

context.SaveChanges();

The changes are applied and the database is updated.

So, why the CreateAsync method "saves its changes" without explicitly calling "context.SaveChanges" and the UpdateAsync method needs it? Is the ApplicationDbContext instance i get by dependency injection the same that CreateAsync is using?

Thank you.

like image 678
MorgoZ Avatar asked Jan 30 '18 10:01

MorgoZ


1 Answers

The default registration of Entity Framework Core in an ASP.NET Core application is a per-request scope, so they will definitively share the context.

As you can see from the source code of the default implementation of EF Core for ASP.NET Core Identity (here

/// <summary>
/// Creates the specified <paramref name="user"/> in the user store.
/// </summary>
/// <param name="user">The user to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
public async override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
    cancellationToken.ThrowIfCancellationRequested();
    ThrowIfDisposed();
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user));
    }
    Context.Add(user);
    await SaveChanges(cancellationToken);
    return IdentityResult.Success;
}

and here

/// <summary>
/// Updates the specified <paramref name="user"/> in the user store.
/// </summary>
/// <param name="user">The user to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
public async override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
    cancellationToken.ThrowIfCancellationRequested();
    ThrowIfDisposed();
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user));
    }

    Context.Attach(user);
    user.ConcurrencyStamp = Guid.NewGuid().ToString();
    Context.Update(user);
    try
    {
        await SaveChanges(cancellationToken);
    }
    catch (DbUpdateConcurrencyException)
    {
        return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
    }
    return IdentityResult.Success;
}

, they both save changes.

The problem you are experiencing is that you are not awaiting for the result of UpdateAsync, so you need to change to:

await userManager.UpdateAsync(adminTeacherUser);
//or
userManager.UpdateAsync(adminTeacherUser).Wait();

Of course, using async-all-the-way is the preferred version and you shouldn't block using .Wait().

like image 168
Camilo Terevinto Avatar answered Oct 29 '22 22:10

Camilo Terevinto