【问题标题】:Request scope is disposed after calling any async DbContext operation调用任何异步 DbContext 操作后释放请求范围
【发布时间】: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


【解决方案1】:

问题解决了。正如@mjwills 所说 - 我忘了等待异步操作。未等待委托之一中的调用方法。等待_next delegate后,问题就解决了。

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 2019-01-08
    • 1970-01-01
    • 2021-10-13
    • 2017-12-16
    • 2019-12-17
    • 2017-04-18
    • 1970-01-01
    • 2021-01-29
    相关资源
    最近更新 更多