使用 Entity Framework Core 自动增加部分主键

Posted

技术标签:

【中文标题】使用 Entity Framework Core 自动增加部分主键【英文标题】:Auto-increment on partial primary key with Entity Framework Core 【发布时间】:2016-07-09 09:38:16 【问题描述】:

我已经使用 EF Core fluent API 声明了以下模型:

modelBuilder.Entity<Foo>()
    .HasKey(p => new  p.Name, p.Id );

当我在 PostgreSQL 中创建数据库时,这两个字段都标记为主键,但 Id 字段没有标记为自动增量。我也试过添加

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

到 Foo 下的 Id 字段,而不会对迁移代码产生任何影响。有没有办法制作 Id AI 虽然它是 PPK?

【问题讨论】:

你的Id属性是什么类型的? Id 是 int,Name 是字符串 如果Id 是自动生成的数字,为什么还需要将Name 作为主键的一部分? 【参考方案1】:

好吧,那些数据注释应该可以解决问题,也许与 PostgreSQL 提供程序有关。

来自EF Core documentation:

根据所使用的数据库提供程序,可能会生成值 EF 或数据库中的客户端。如果该值是由 数据库,那么 EF 可能会在您添加实体时分配一个临时值 到上下文。然后这个临时值将被替换为 SaveChanges期间数据库生成的值。

你也可以试试这个 Fluent Api 配置:

modelBuilder.Entity<Foo>()
            .Property(f => f.Id)
            .ValueGeneratedOnAdd();

但正如我之前所说,我认为这与数据库提供者有关。尝试向您的数据库添加新行,稍后检查是否为 Id 列生成了值。

【讨论】:

我已将此标记为答案,尽管我的代码尚未完全正常工作。这会在启动时引发异常,说“不支持为 int4 创建 IsIdentity”,所以我猜你是对的,这取决于数据库提供程序,到目前为止,他们似乎还没有实现对它的支持。 只是跟进,使用 PostgreSQL,您无法修改现有字段以成为 AI。这导致了我之前遇到的异常,所以我必须扔掉我的数据库,创建一个新的并放入所有数据。【参考方案2】:

首先,您不应该将 Fluent Api 与数据注释合并,因此我建议您使用以下之一:

确保您已正确设置密钥

modelBuilder.Entity<Foo>()
            .HasKey(p => new  p.Name, p.Id );
modelBuilder.Entity<Foo>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

或者您也可以使用数据注释来实现它

public class Foo

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key, Column(Order = 0)]
    public int Id  get; set; 

    [Key, Column(Order = 1)]
    public string Name get; set; 

【讨论】:

不是他在fluent api中定义的时候,顺序是匿名对象中属性的顺序 HasDatabaseGeneratedOption 在我使用的版本(7.0.0-rc1-final)中似乎不存在,还是我缺少扩展或什么? 我认为这不能解决 OP 的问题。他正在将 Fluent Api(他在其中建立 Ids 顺序)与 Data Annotations(他指定 Id 属性为 Identity)合并。我个人不喜欢合并这两个功能,但他的配置应该可以工作。我认为正在发生的事情更多地取决于数据库提供者。 @MassimilianoPeluso 但这不是问题,那部分首先在 OP 的代码中 我试图坚持使用流畅的 API,但据我所知,HasDatabaseGeneratedOption 仅存在于 EF6 而不是 EF7。还是我需要使用扩展程序才能访问该功能?【参考方案3】:

致所有遇到此问题的人,他们使用 SQL Server 数据库,即使在 int 主键上添加以下注释后仍然抛出异常

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id  get; set; 

请检查您的 SQL,确保您的主键旁边有 'IDENTITY(startValue, increment)',

CREATE TABLE [dbo].[User]
(
    [Id] INT IDENTITY(1,1) NOT NULL PRIMARY KEY
)

这将使数据库在每次添加新行时增加id,起始值为1,增量为1。

我不小心忽略了在我的 SQL 中花费了我一个小时的生命, 所以希望这对某人有帮助!!!

【讨论】:

我仍然面临错误“当 IDENTITY_INSERT 设置为 OFF 时,无法在表 'User' 中插入标识列的显式值。” 我遇到同样的错误,我有 Id 和 Version 的主键,在更新时我想通过增加我的版本但保留 Id 来添加新条目,还没有找到一个好的解决方案。【参考方案4】:

像下面这样注释属性

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID  get; set; 

要为新模型上的所有值生成属性使用标识列,只需将以下内容放在上下文的 OnModelCreating() 中:

builder.ForNpgsqlUseIdentityColumns();

这将使所有具有 .ValueGeneratedOnAdd() 的键和其他属性默认具有标识。您可以使用 ForNpgsqlUseIdentityAlwaysColumns() 来始终拥有 Identity,还可以使用 UseNpgsqlIdentityColumn() 和 UseNpgsqlIdentityAlwaysColumn() 逐个属性地指定身份。

postgres efcore value generation

【讨论】:

.netcore EF最新版本下,不推荐使用ForNpgsqlUseIdentityColumns,使用下面一行builder.UseIdentityColumns【参考方案5】:

指定列类型为serial以便PostgreSQL生成id。

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order=1, TypeName="serial")]
public int ID  get; set; 

https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-SERIAL

【讨论】:

以上是关于使用 Entity Framework Core 自动增加部分主键的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 性能优化

在 Entity Framework Core 中使用 [ComplexType]

在 Entity Framework Core 中使用 SQL 视图

使用 Entity Framework Core 更新相关数据

使用 Entity Framework Core 自动增加部分主键

如何使用 Entity Framework Core 模拟异步存储库