实体框架自动生成GUID

use*_*298 39 c# asp.net-mvc entity-framework

我是EF新手,所以这里.我有一个包含以下内容的类

public class EmailTemplate
{
    public Guid Id { get; set; }

    [MaxLength(2000)]
    public string Html { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是我的映射类

class EmailMapper : EntityTypeConfiguration<EmailTemplate>
    {
        public EmailMapper()
        {
            ToTable("EmailTemplate");

            HasKey(c => c.Id);
            Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(c => c.Id).IsRequired();
        }
    }
Run Code Online (Sandbox Code Playgroud)

我想打电话DbContext.SaveChanges(),但是我收到以下错误:

异常详细信息:System.Data.SqlClient.SqlException:无法将值NULL插入列'Id',表'AutoSendConnection.dbo.EmailTemplates'; 列不允许空值.INSERT失败.

我究竟做错了什么?为什么EF auto不会创建一个独特的GUID?

Mar*_*ark 53

只需在EmailTemplate类上装饰Id字段,如下所示,SQL Server将自动生成插入值.

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
Run Code Online (Sandbox Code Playgroud)

您也可以删除不再需要的Mapper类.

  • 这在 EF Core 中不起作用。迁移文件没有此 Id 列的任何标识信息。 (3认同)
  • 如果您使用 CodeFirst,EF 可能无法检测到此更改。您可以删除表并添加新的迁移。 (2认同)

Moh*_*reh 21

使用流畅的API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Node>().Property(x => x.ID).HasDefaultValueSql("NEWID()");
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您使用MSSql,然后使用内存中的Sql Lite进行测试,则此方法将无效。 (2认同)

War*_*War 18

在此处解决其他答案

这些其他选项似乎都不起作用,我一次又一次地与 EF 团队在 github 上提出质疑......

https://github.com/aspnet/EntityFramework6/issues/762

...出于某种原因,EF 开发团队似乎认为这是“按设计工作”并反复关闭质疑此“错误”的票证。

EF团队解释

出于某种原因,他们似乎认为“在 SQL 中生成 Guid 被认为不是最佳实践,为了确保密钥立即可用,我们应该在应用程序代码中生成密钥”。

当然,这里的问题是高度填充的表会冒着采取进一步业务操作的风险,使用无效的密钥。

在我的情况下,这可能会破坏一些极其复杂的多服务器 DTC 事务,所以我不相信 MS 的建议是正确的,即 EF Core 目前根本不支持分布式事务,因此在重点关注的上下文中,他们可能有一点.

我的答案(实际上有效)

简而言之,我通过在生成迁移后“手动破解”生成的迁移来解决这个问题......

EF 代码第一次迁移,DB 生成的 guid 密钥

引用另一个问题的答案如下:

像通常将两个属性放在键属性上一样生成迁移脚本,如下所示...

public class Foo
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public Guid Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

...声明性地说,实体现在是正确的。

它将生成的迁移看起来像:

CreateTable(
    "dbo.Foos",
    c => new
        {
            Id = c.Guid(nullable: false),
            ...
        })
    .PrimaryKey(t => t.Id)
    ...;
Run Code Online (Sandbox Code Playgroud)

...我无法确定原因,但在某些情况下这会起作用,而在其他情况下则不会(运行迁移,执行插入以进行测试)。

如果失败,请回滚迁移然后修改它以读取类似...

CreateTable(
    "dbo.Foos",
    c => new
        {
            Id = c.Guid(nullable: false, defaultValueSql: "newid()"),
            ...
        })
    .PrimaryKey(t => t.Id)
    ...;
Run Code Online (Sandbox Code Playgroud)

...这里的额外代码告诉 SQL 按照我们的预期生成密钥。

根据经验,出于一致性原因,我会一直应用此更改,这意味着一目了然,您的迁移将准确显示数据库生成了哪些键,当然哪些不是。

  • 这应该标记为答案。太感谢了! (2认同)
  • 此解决方案的问题是您必须知道或记住在每次创建新表时添加它。希望这件事能够得到解决。 (2认同)

WoI*_*IIe 9

经过长时间的调查,我发现在 EF Core 3.1 中你需要使用

builder.Property(e => e.Id).ValueGeneratedOnAdd();
Run Code Online (Sandbox Code Playgroud)

  • @misha 您使用哪个数据库提供商? (2认同)

Liv*_* M. 5

在映射配置中将字段的默认 sql 值设置为“newsequentialid()”。