带有Cosmos DB提供程序的EF Core,UserManager AddLoginAsync导致ConcurrencyFailure [英] EF Core with Cosmos DB provider, UserManager AddLoginAsync gives ConcurrencyFailure

查看:138
本文介绍了带有Cosmos DB提供程序的EF Core,UserManager AddLoginAsync导致ConcurrencyFailure的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

创建一个新用户,使用usermanager再次获取该用户以进行测试,然后对最近获取的用户使用方法AddLoginAsync给出错误

Creating a new user, fetching the user again with usermanager for testing, and then using the method AddLoginAsync with the recently fetched user gives the error

ConcurrencyFailure,乐观并发失败,对象已被修改.

ConcurrencyFailure, Optimistic concurrency failure, object has been modified.

在获取用户时,"ConcurrencyStamp"具有正确的etag,但在"AddLoginAsync"之后,我可以看到用户对象的etag无效,ConcurrencyStamp是一个GUID.

When fetching the user the "ConcurrencyStamp" has the correct etag, but after the "AddLoginAsync" I can see the user object has an invalid etag, the ConcurrencyStamp is a GUID.

我已经遵循了文档,并将其添加到IdentityUser模型配置中:

I have followed the documentation and added this to the IdentityUser model configuration:

//builder: EntityTypeBuilder<ApplicationUser>
builder.Property(d => d.ConcurrencyStamp)
       .IsETagConcurrency();

启动:

services.AddDbContextPool<ApplicationDbContext>(option =>
{
    var connectionConfig = new DbConnectionString("DefaultConnection", Configuration);
    option.UseCosmos(connectionConfig.ConnectionString, connectionConfig.Database);
});

services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

在Cosmos DB仿真器中,我可以看到创建的两个文档:

In the Cosmos DB Emulator I can see two documents created:

{
    "Id": "b66b520e-60d2-4ba3-8347-6c168be9a1e5",
    "AccessFailedCount": 0,
    "_etag": "\"00000000-0000-0000-d54b-cffa72ea01d6\"",
    "Created": "2020-12-18T14:41:03.962769Z",
    "Discriminator": "ApplicationUser",
    "Email": "@email.com",
    "EmailConfirmed": false,
    "Encounters": 0,
    "LockoutEnabled": true,
    "LockoutEnd": null,
    "Nickname": null,
    "NormalizedEmail": "@EMAIL.COM",
    "NormalizedUserName": "@EMAIL.COM",
    "OrderNum": 0,
    "PasswordHash": null,
    "PhoneNumber": null,
    "PhoneNumberConfirmed": false,
    "SecurityStamp": "PPFUV4JXJ72KBYNXBPPV3FVJMIG",
    "TwoFactorEnabled": false,
    "UserName": "@email.com",
    "id": "ApplicationUser|b66b520e-60d2-4ba3-8347-6c168be9a1e5",
    "_rid": "VA5JAKKvHtQNAAAAAAAAAA==",
    "_self": "dbs/VA5JAA==/colls/VA5JAKKvHtQ=/docs/VA5JAKKvHtQNAAAAAAAAAA==/",
    "_attachments": "attachments/",
    "_ts": 1608302464 }

{
    "LoginProvider": "Microsoft",
    "ProviderKey": "***",
    "Discriminator": "IdentityUserLogin<string>",
    "ProviderDisplayName": "Microsoft",
    "UserId": "b66b520e-60d2-4ba3-8347-6c168be9a1e5",
    "id": "IdentityUserLogin<string>|Microsoft|***",
    "_rid": "VA5JAKKvHtQOAAAAAAAAAA==",
    "_self": "dbs/VA5JAA==/colls/VA5JAKKvHtQ=/docs/VA5JAKKvHtQOAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-d54b-d4ad146f01d6\"",
    "_attachments": "attachments/",
    "_ts": 1608302472
}

如何解决此问题?

推荐答案

我可以通过创建新的UserStore来解决此问题,

I could fix the issue by creating a new UserStore,

   public class ApplicationUserStore 
        : UserStore<ApplicationUser,ApplicationRole,ApplicationDbContext>
    {
        public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null)
            : base(context, describer)
        {
        }

        public override async Task<IdentityResult> UpdateAsync(ApplicationUser user, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            Context.Attach(user);
            Context.Update(user);
            try
            {
                await SaveChanges(cancellationToken);
            }
            catch (DbUpdateConcurrencyException)
            {
                return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
            }

            return IdentityResult.Success;
        }
    }

但是在使用signinmanager时,我现在面临另一个问题:

But I am now facing another issue when using signinmanager:

InvalidOperationException:LINQ表达式'DbSet< IdentityUserRole>().Join(内部:DbSet(),outerKeySelector:i =>i.RoleId,innerKeySelector:a =>a.Id,resultSelector:(i,a)=>新的TransparentIdentifier< IdentityUserRole,ApplicationRole>(外部= i,内部= a))'无法翻译.要么重写以可以翻译的形式查询,或切换到客户评估通过插入对"AsEnumerable","AsAsyncEnumerable"的调用来显式地"ToList"或"ToListAsync".看https://go.microsoft.com/fwlink/?linkid=2101038,以获取更多信息.

InvalidOperationException: The LINQ expression 'DbSet<IdentityUserRole>() .Join( inner: DbSet(), outerKeySelector: i => i.RoleId, innerKeySelector: a => a.Id, resultSelector: (i, a) => new TransparentIdentifier<IdentityUserRole, ApplicationRole>( Outer = i, Inner = a ))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

我通过覆盖UserStore中的另一种方法找到了解决方案:

I found the solution by overriding another method in the UserStore:

public override async Task<IList<string>> GetRolesAsync(ApplicationUser user, CancellationToken cancellationToken = default)
{
    string userId = user.Id;
    var roleIds = await Context.UserRoles.Where(ur => ur.UserId == userId)
        .Select(ur => ur.RoleId)
        .ToArrayAsync();

    return await Context.Roles.Where(r => roleIds.Contains(r.Id))
        .Select(r => r.Name)
        .ToListAsync();
}

这篇关于带有Cosmos DB提供程序的EF Core,UserManager AddLoginAsync导致ConcurrencyFailure的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆