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
的结果你不是await
ing,所以你需要改成:
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 () => await userManager.UpdateAsync(adminTeacherUser)).GetAwaiter().GetResult()
,而不是仅仅调用Wait()
。以上是关于ASP.NET 核心标识。使用 ApplicationDbContext 和 UserManager。他们是不是共享上下文?的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core 标识不注入 UserManager<ApplicationUser>
为啥新的 ASP.NET 标识表停止使用 Guid(唯一标识符类型)作为键?