c# - EF Core 拥有的实体影子 PK 导致 SQLite 违反空约束

标签 c# sqlite entity-framework-core ef-core-5.0

我有一个 Comment 拥有的实体类型:

public class Comment {    // owned entity type
  public Comment(string text) { Text = text; }
  public string Text { get; private set; }
}

public class Post {
  public Post(string content) { Content = content; }
  public long Id { get; private set; }
  public string Content { get; private set; }
  public ICollection<Comment> Comments { get; private set; } = new HashSet<Comment>();
}

Post的配置包括:

builder.OwnsMany(x => x.Comments, x => {
  x.Property(y => y.Text).IsRequired();
});

种子代码包括:

var post = new Post("content");
post.Comments.Add(new Comment("comment1"));
post.Comments.Add(new Comment("comment2"));
await _context.AddAsync(post);
await _context.SaveChangesAsync();

当我使用 postgres 提供程序时,我可以成功地创建、播种和编辑数据库。

当我使用 sqlite 提供程序时,我可以成功创建数据库,但是当我尝试为它做种时,我得到了这个错误:

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'NOT NULL constraint failed: Comment.Id'.

docs say拥有的表有一个隐含的键,这解释了关于 Comment.Id 的提示。

但是为什么这只发生在 sqlite 上,我该如何解决它

最佳答案

这是由 (1) 不正确的 (恕我直言) EF Core 默认值和 (2) 不受支持的 SQLite 功能共同造成的。

  1. Collections of owned types 中所述EF Core 文档

Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance, as we do for OwnsOne: there can be multiple owned type instances for each owner, and hence the key of the owner isn't enough to provide a unique identity for each owned instance.

问题在于,如果你没有定义显式 PK,那么 EF Core 会生成名为 Id 的影子属性(列),键入 int,自动递增(他们认为, 但是参见 (2)) 并在 (OwnerId, Id) 上定义 复合 PK

  1. 然而,SQLite 支持自动增量列如果它是单个 PK 列。因此,它会生成常规的 INTId,然后需要 INSERT 上的显式值,但 EF Core 不会发送它,因为它仍然认为属性是在服务器上自动生成的。

话虽如此,您最好始终定义拥有的集合实体的 PK。由于自动增量本身是唯一的,因此绝对最小值是将自动生成的阴影 Id 属性标记为 PK,例如

builder.OwnsMany(e => e.Comments, cb => {
    cb.HasKey("Id"); // <-- add this
    // The rest...
    cb.Property(e => e.Text).IsRequired();
});

生成的迁移应该有 Id 列的“Sqlite:Autoincrement”注释:

Id = table.Column<long>(type: "INTEGER", nullable: false)
    .Annotation("Sqlite:Autoincrement", true),

在 OP 设计中缺少并导致问题。

我个人更喜欢 EF Core 抛出常规的无键定义错误,而不是定义所有数据库都不支持的 PK 构造。 SQLite 提供程序也抛出异常,而不是默默地忽略自动增量模型请求,从而引入模型元数据之间的差异(EF Core 基础设施使用它来控制所有运行时行为)。因此,从技术上讲,两者都可以被视为错误。但他们就是他们。一般来说,约定优于配置,但对于具有任意默认值的事物要明确。

关于c# - EF Core 拥有的实体影子 PK 导致 SQLite 违反空约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69819523/

相关文章:

c# - 如何在 EF Core 2.0 中使用 IEntityTypeConfiguration 设置 ForeignKey 和 Index 属性

c# - 中断更改后如何在 EF Core 3.1 中创建复杂的动态查询?

C# Gecko 选择特定的列表框或组合框

c# - RegularExpressionValidator ErrorMessage 边界溢出两行

java - 从 Java 对象更新/合并 SQLite 数据库表

ios - NSManagedObjectContext 没有正确保存到 SQLite

c# - ASP.NET 5 多个 dbcontext 问题

c# - 在 PowerShell 中调用通用静态方法

c# - 增加 asp.net 应用程序的应用程序超时

c# - 如何以编程方式获取Firefox history.sqlite文件的路径?