使用Entity Framework Core实现部分主键自增

91

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

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

在我创建PostgreSQL数据库时,这两个字段都被标记为主键,但是“Id”字段没有被标记为自增。我也尝试过添加

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

是否有一种方法可以使Id字段成为AI,而不会对迁移代码产生任何影响,即使它是PPK也可以?


你的 Id 属性是什么类型? - ocuenca
Id是一个整数,Name是一个字符串。 - Martin
1
如果Id是自动生成的数字,为什么还需要将Name作为主键的一部分呢? - D Stanley
5个回答

112
这些数据注释应该能起到作用,也许与PostgreSQL提供程序有关。
来自EF Core文档
根据所使用的数据库提供程序不同,值可能会在EF中由客户端生成或在数据库中生成。如果值是由数据库生成的,则EF在将实体添加到上下文时可能会分配一个临时值。此临时值将在SaveChanges期间被数据库生成的值替换。
您还可以尝试使用此流畅API配置:
modelBuilder.Entity<Foo>()
            .Property(f => f.Id)
            .ValueGeneratedOnAdd();

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


尽管我的代码还没有完全运行,但我已经将其标记为答案。这会在启动时抛出异常,显示“不支持为int4创建IsIdentity”,所以我猜你是对的,这取决于数据库提供程序,到目前为止,它似乎还没有实现对其的支持。 - Martin
2
只是跟进一下,使用PostgreSQL无法修改现有字段成为人工智能字段。这导致了我之前遇到的异常,所以我不得不放弃我的数据库,创建一个新的并将所有数据放入其中。 - Martin

36

首先,您不应将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; }
}

当他在流畅的 API 中定义它时,顺序是匿名对象中属性的顺序。 - Alexander Derck
28
在我使用的版本(7.0.0-rc1-final)中好像没有HasDatabaseGeneratedOption这个选项,或者是我缺少某个扩展程序之类的东西吗? - Martin
2
我认为这并不能解决原帖作者的问题。他正在将Fluent API(在此处确立Ids顺序)与数据注释(在此处指定“Id”属性为Identity)合并。个人而言,我不喜欢将这两个特性合并,但他的配置应该可以工作。我认为发生的情况更取决于数据库提供程序。 - ocuenca
1
@MassimilianoPeluso 但这不是问题,这部分在第一位的原始代码中就是正确的。 - Alexander Derck
7
我尽力保持使用流畅的API,但据我所知,HasDatabaseGeneratedOption仅存在于EF6而不是EF7。是否有需要使用扩展来访问该功能? - Martin
显示剩余3条评论

21

如果你正在使用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' 表中无法插入明确的标识列值。” - Praveen Patel G
我遇到了相同的错误,我的主键是Id和Version,在更新时,我想通过递增版本号来添加新条目,但保留Id,但还没有找到好的解决方案。 - Ludvig W

13

请按如下方式注释该属性

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

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

builder.ForNpgsqlUseIdentityColumns();

这将默认创建所有具有.ValueGeneratedOnAdd()的键和其他属性都具有标识。您可以使用ForNpgsqlUseIdentityAlwaysColumns()始终具有标识,也可以使用UseNpgsqlIdentityColumn()和UseNpgsqlIdentityAlwaysColumn()对每个属性进行标识。

PostgreSQL EF Core 值生成


1
在最新版本的.netcore EF中,ForNpgsqlUseIdentityColumns已被弃用,请使用以下代码builder.UseIdentityColumns。 - Vlad Hrona

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接