在 Blazor 服务器端应用程序中违反主键约束“PK_AspNetUsers”

Posted

技术标签:

【中文标题】在 Blazor 服务器端应用程序中违反主键约束“PK_AspNetUsers”【英文标题】:Violation of PRIMARY KEY constraint 'PK_AspNetUsers' in a Blazor server-side app 【发布时间】:2022-01-12 20:00:37 【问题描述】:

我知道这个问题已被问过很多次,但我看到的所有答案似乎都与来自与当前上下文不同的上下文相关的实体有关,例如 ASP.NET 等客户端/服务器场景MVC。

在我的情况下,这不是真的,因为所有操作都发生在一个代码块中。如果这有什么不同的话,这一切都在 Blazor 服务器端应用程序中。

我正在使用 ASP.NET Identity,身份模型名为 User。我有一个History 模型(为了清楚起见,删除了不相关的属性)...

public class History 
  public int Id  get; set; 
  public string UserID  get; set; 
  [ForeignKey(nameof(UserID))]
  public virtual User User  get; set; 

我正在尝试创建一个新的History 项目,如下所示...

string email = (await AuthenticationStateProvider.GetAuthenticationStateAsync())
  .User.Identity.Name.ToLower();
User user = await Context.Users.SingleAsync(u => u.Email.ToLower() == email);
History h = new() 
  // Other properties removed for clarity
  UserID = user.Id,
;
Context.Histories.Add(h);
await Context.SaveChangesAsync();

...但这会引发异常...

违反主键约束“PK_AspNetUsers”。无法在对象“dbo.AspNetUsers”中插入重复键。重复的键值是...

如您所见,所有代码都发生在一个块中,因此从数据库中检索到的 User 实体和我试图保存的新创建的 History 实体都应该处于相同的上下文中。我不明白它为什么要尝试添加新的User

上下文以常规方式注入到 Blazor 组件中...

[Inject]
private ApplicationDbContext Context  get; set; 

...在Startup.cs中设置如下...

services.AddDbContext<ApplicationDbContext>(options => 
  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
  options.EnableSensitiveDataLogging();
  options.EnableDetailedErrors();
, ServiceLifetime.Transient);

这是一个相当成熟的应用程序,在其他地方做这种事情似乎都很好,只是这个新的History 项目导致了异常,即使我们在其他地方做同样的事情。

我试过了……

将用户状态设置为不变 检索用户时添加AsNoTrackingWithIdentityResolution() 设置User 属性而不是UserId 属性 保存前将User 属性设置为null

...但是这些都没有帮助。

作为一个实验,我尝试注入第二个上下文,并使用它来获取用户 ID,然后将其处理并将其设置为 null,以确保它没有闲置。在这个阶段,原始上下文根本没有触及用户,我有一个带有所需 Id 的纯字符串。但是,我仍然遇到了同样的异常。

有人有什么想法吗?谢谢

【问题讨论】:

上面的代码,单独来看,对我来说似乎很好。该上下文是否在其他地方使用?它是新注入控制器的吗?可以显示 Startup 的 Context 注册吗? @NeilW 上下文被注入到组件中(请参阅我更新的问题),并在其他地方使用,但仅在此组件中使用。我展示的代码是唯一可以访问用户的地方。我还添加了来自Startup.cs 的代码,但我认为这不会有帮助,因为它是沼泽标准。谢谢 我询问了这家初创公司,因为最有可能的原因是上下文的生命周期以及从其他逻辑片段中跟踪的其他实体。即使它是瞬态的,它在组件的生命周期内仍然是相同的实例,如果您在组件的“其他地方”使用它,这是最可能的原因。调整您的设计以确保 DbContext 是短暂的(即对于每个事务)。您很少需要在同一上下文中调用 SaveChanges 两次。或者...在每个地方执行 SaveChanges 之后,立即调用 Context.ChangeTracker.Clear()。 @NeilW 感谢您的评论,但我们几乎总是在各个地方都有调用 SaveChangesAsync 的组件,而且我们以前从未遇到过这个问题。碰巧的是,这是第一次(也是迄今为止唯一一次)在此组件中调用它。到目前为止,组件所做的只是从上下文中读取数据。不知道这会把我留在哪里。再次感谢,还有什么想法吗? @NeilW 另请参阅我刚刚对有关实验的问题所做的更新。我真的不明白为什么现在会发生这种情况。 【参考方案1】:

唉,原来我不仅找错树了,还找错了森林!

问题与 History 项目本身的 UserId 属性无关,它是具有导航属性的被省略(看似无害)的其他属性之一(通过参数传入)对 user 的引用,这导致了异常。

我改了代码忽略参数,直接从数据库中获取其他属性,现在一切正常。

不知道这是否会帮助任何人,但我想我会发布它作为答案以防万一。

【讨论】:

以上是关于在 Blazor 服务器端应用程序中违反主键约束“PK_AspNetUsers”的主要内容,如果未能解决你的问题,请参考以下文章

违反主键约束查询

ORA-00001: 违反唯一约束条件

如何在不违反主键约束的情况下插入具有循环引用的实体框架

ORA-02292违反完整约束和ORA-02297无法禁用约束条件 cascade禁用主键

Hibernate 和 HSQLDB 违反主键完整性约束

System.Data.SqlClient.SqlException:'违反主键约束