EF Core 与 Cosmos DB 提供程序,UserManager AddLoginAsync 提供 ConcurrencyFailure [英] EF Core with Cosmos DB provider, UserManager AddLoginAsync gives ConcurrencyFailure

查看:18
本文介绍了EF Core 与 Cosmos DB 提供程序,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() .Join( 内部:DbSet(), 外键选择器: i =>i.RoleId,内部键选择器:a =>a.Id, resultSelector: (i, a) =>新的TransparentIdentifier(Outer = i, Inner = 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();
}

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

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