EF Core 与 Cosmos DB 提供程序,UserManager AddLoginAsync 提供 ConcurrencyFailure [英] EF Core with Cosmos DB provider, UserManager AddLoginAsync gives 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
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屋!