【发布时间】:2021-08-10 12:55:53
【问题描述】:
我正面临由使用 EF Core 的数据库引起的问题。简而言之:对数据库的每个异步操作都会引发异常:“任务已取消。”。我试图使用 IServiceProvider 中的另一个范围来创建上下文并运行异步代码,但是异步方法的第一次调用正在关闭请求并且代码已经在运行,但是请求已经结束。在控制器中执行代码或在代码中的其他位置执行代码没有区别。更详细地说,这些问题是在我用“Identity”实体扩展我的 DbContext 并与“Profile”实体建立关系之后开始的。我不明白,这种应用程序行为的原因是什么,即使对数据库的同步操作运行良好。
不工作代码的例子是:
try
{
var userExists = await _userManager.Users
.AnyAsync(x => x.Email == request.Email, cancellationToken: cancellationToken);
...
}
catch(TaskCanceledException ex)
{
// exception details
}
我的上下文如下所示:
public class ApplicationContext : IdentityDbContext<Identity, IdentityRole<Guid>, Guid>
{
...,
public DbSet<Profile> Profiles { get; set;
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Ignore<IdentityUserLogin<Guid>>();
builder.Ignore<IdentityUserToken<Guid>>();
builder.Entity<IdentityUserClaim<Guid>>().ToTable("IdentitiesClaims");
builder.Entity<IdentityRoleClaim<Guid>>().ToTable("IdentitiesRoleClaims");
builder.Entity<IdentityRole<Guid>>().ToTable("IdentitiesRoles");
builder.Entity<IdentityUserRole<Guid>>().ToTable("IdentitiesUserRoles");
builder.Entity<Identity>.ToTable("Identities");
builder.Entity<Identity>.HasIndex(x => new { x.Id, x.Email
}).IsUnique();
builder.Entity<Identity>.Ignore(x => x.AccessFailedCount);
builder.Entity<Identity>.Ignore(x => x.LockoutEnabled);
builder.Entity<Identity>.Ignore(x => x.LockoutEnd);
builder.Entity<Identity>.Ignore(x => x.PhoneNumber);
builder.Entity<Identity>.Ignore(x => x.TwoFactorEnabled);
builder.Entity<Identity>.Ignore(x => x.PhoneNumberConfirmed);
builder.Entity<Identity>.Ignore(x => x.UserName);
builder.Entity<Profile>.HasKey(x => x.Id);
}
}
身份实体:
public sealed class Identity : IdentityUser<Guid>
{
public IdentityType IdentityType { get; set; }
public string RefreshToken { get; set; }
public DateTime CreatedOn { get; set; }
public Profile Profile { get; set; }
private Identity() { }
public Identity(Guid id, string email, string passwordHash,
IdentityType identityType, string refreshToken)
{
this.Id = id;
this.Email = email;
this.UserName = email;
this.EmailConfirmed = true;
this.NormalizedEmail = email.ToUpper();
this.NormalizedUserName = this.NormalizedEmail;
this.PasswordHash = passwordHash;
this.IdentityType = identityType;
this.RefreshToken = refreshToken;
this.CreatedOn = DateTime.UtcNow;
}
public static Identity FromDomain(Domain.Identity.Identity identity, string refreshToken)
=> new(identity.Id, identity.Email, identity.PasswordHash, identity.IdentityType, refreshToken);
}
个人资料实体:
public class Profile : IEntity<int>
{
public int Id { get; protected set; }
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public DateTime DateOfBirth { get; protected set; }
public Guid IdentityId { get; protected set; }
protected Profile() { }
public Profile(string firstName, string lastName,
DateTime dateOfBirth, Guid? identityId = null, int? id = null)
{
this.FirstName = firstName;
this.LastName = lastName;
this.DateOfBirth = dateOfBirth;
if (id.HasValue)
{
this.Id = id.Value;
}
if (identityId.HasValue)
{
this.IdentityId = identityId.Value;
}
}
}
抛出 TaskCanceledException 的原因可能是什么?如何异步运行数据库操作?
@Edit - 15:34 我最小的可重现示例是: 程序.cs -
public static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webHost =>
{
webHost.CaptureStartupErrors(true)
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel();
}).Build();
await host.RunAsync();
}
身份控制器 -
public async Task<IActionResult> BeginUserCreation(string email)
{
await _commandBus.Send(new BeginUserCreation(email));
return this.Ok();
}
CommandBus -
public Task Send<TCommand>(TCommand command) where TCommand : ICommand
=> Mediator.Send(command);
CommandHandler -
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand>
where TCommand : ICommand { }
BeginUserCreationHandler -
public async Task<Unit> Handle(BeginUserCreation request, CancellationToken cancellationToken)
{
try
{
var userExists = await _userManager.Users
.AnyAsync(x => x.Email == request.Email, cancellationToken: cancellationToken);
...
}
catch(TaskCanceledException ex)
{
return Unit.Value;
}
}
【问题讨论】:
-
我猜,在您的 startup.cs 中,您将 DbContext 注册为单例。 DbContext 应该是 Scoped 的。
-
Web 服务器可能会取消异步任务。从哪里获得
cancellationToken? -
@Neil - DbContext 已注册为作用域。 @S
-
@SvyatoslavDanyliv - 在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancelToken) 处引发了 TaskCanceledException
-
您在某处忘记了
await。分享minimal reproducible example,我们可以告诉你在哪里。
标签: c# .net entity-framework entity-framework-core