ASP.NET 核心标识。使用 ApplicationDbContext 和 UserManager。他们是不是共享上下文?

Posted

技术标签:

【中文标题】ASP.NET 核心标识。使用 ApplicationDbContext 和 UserManager。他们是不是共享上下文?【英文标题】:ASP.NET Core Identity. Using ApplicationDbContext and UserManager. Do they share the context?ASP.NET 核心标识。使用 ApplicationDbContext 和 UserManager。他们是否共享上下文? 【发布时间】:2018-07-09 05:48:06 【问题描述】:

我在 ASP.NET MVC Core 2 应用程序中有一个数据库初始化方法。 在那个方法中,我使用 ApplicationDbContext 和 UserManager 来初始化数据库。 我从构造函数的依赖注入中获得了这两个实例:

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

我想知道的是它们是否共享相同的上下文实例,或者是否为 ApplicationDbContext 创建了一个上下文,而为 UserManager 创建了另一个上下文。 如果我执行这样的操作:

ApplicationUser adminTeacherUser = new ApplicationUser
            
                UserName = "test@test.com",
                Email = "test@test.com",
                EmailConfirmed = true,
                Name = "test",
                LastName = "test",
                BirthDate = null,
                EntryDate = DateTime.Now                    
            ;

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

在 CreateAsync 调用之后立即在数据库中创建用户。

但是,如果我这样更新用户:

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

在调用 UpdateAsync 之后,数据库中没有任何更新。用户名依然是“test”。

但是,如果然后我执行:

context.SaveChanges();

应用更改并更新数据库。

那么,为什么 CreateAsync 方法“保存其更改”而不显式调用“context.SaveChanges”而 UpdateAsync 方法需要它?我通过依赖注入获得的 ApplicationDbContext 实例是否与 CreateAsync 使用的相同?

谢谢。

【问题讨论】:

它们在请求范围内使用相同的上下文。 也不要进行阻塞调用.Wait(),在异步方法中使用await,否则会有死锁的风险。 【参考方案1】:

Entity Framework Core 在 ASP.NET Core 应用程序中的默认注册是每个请求的范围,因此它们将最终共享上下文。

从 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;

和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;

,它们都保存更改。

你遇到的问题是UpdateAsync的结果你不是awaiting,所以你需要改成:

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

当然,使用 async-all-the-way 是首选版本,您不应使用 .Wait() 阻止。

【讨论】:

我会更快地建议不要完全使用该阻塞调用。 即使他没有await 调用UpdateAsync,它不应该将更改保存到数据库吗?代码不会等到保存命令完成,但我相信它应该保存。 @Alisson 这完全取决于调用UpdateAsync 后会发生什么。如果请求在此处结束,则上下文可能会在有机会保存更改之前被丢弃 非常感谢。我正在使用 .Wait() 调用,因为这是一个 DB Initializer 方法,作为从 Program.Main 调用的 WebHost 的扩展完成;所以这里没有 await 可用。无论如何,那是主要用于测试的开发代码。 @MorgoZ 在这种情况下最好使用Task.Run(async () =&gt; await userManager.UpdateAsync(adminTeacherUser)).GetAwaiter().GetResult(),而不是仅仅调用Wait()

以上是关于ASP.NET 核心标识。使用 ApplicationDbContext 和 UserManager。他们是不是共享上下文?的主要内容,如果未能解决你的问题,请参考以下文章

带有身份的asp.net核心中的依赖注入错误

ASP.NET Core 标识不注入 UserManager<ApplicationUser>

为啥新的 ASP.NET 标识表停止使用 Guid(唯一标识符类型)作为键?

使用 SQL 脚本创建 ASP.NET 标识表

无法使用 ASP.NET Core 从 JWT 令牌获取声明

ASP.NET 标识和声明