【问题标题】:Entity Framework auto generate GUID实体框架自动生成 GUID
【发布时间】:2014-08-02 11:51:51
【问题描述】:

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

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

    [MaxLength(2000)]
    public string Html { get; set; }
}

这是我的映射类

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

            HasKey(c => c.Id);
            Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(c => c.Id).IsRequired();
        }
    }

我正在尝试拨打DbContext.SaveChanges(),但收到以下错误:

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

我做错了什么?为什么 EF 不会自动创建唯一的 GUID?

【问题讨论】:

  • 您可以通过 fluent api 设置列的默认值或手动修改或创建迁移 :) 对于 SQL Server,只需使用 newid()newsequentialid() 作为默认值 ????在 PostgreSQL 中你有很多不同的选择,但 uuid_generate_v1() 基本上应该可以解决问题。不幸的是,我相信在 MySQL 和 SQLite 中没有简单的解决方案。

标签: c# asp.net-mvc entity-framework


【解决方案1】:

只需如下装饰您的 EmailTemplate 类上的 Id 字段,SQL Server 将在插入时自动生成值。

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }

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

【讨论】:

  • 实际上,这与 OP 在其代码中所做的相同。尽管这是解决问题的解决方法,但它并不能解决原始问题。
  • 如果您使用 CodeFirst,EF 可能不会检测到此更改。您可以删除表并添加新的迁移。
  • 在生成的迁移中没有设置默认 sql 值,因此这不起作用(即使从头开始创建表)。
  • 此答案仅适用于 EF Core ... EF6 用户会发现此问题。
  • 这在 EF Core 中不起作用。迁移文件没有此 Id 列的任何身份信息。
【解决方案2】:

如果使用 .Net 核心,那么这应该适合您...

使用流畅的 API

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

modelBuilder.Entity<Student>().Property(p => p.StudentID)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

这里有更全面的Cheat Sheet for entity framework

【讨论】:

  • 什么是节点?这里它说它可能缺少一个程序集。我正在使用 EF 6.1。编辑:这是模型。
  • 我收到“HasDefaultValueSql”错误,它在 EF 6.1 上不再存在。我正在寻找替代方案
  • @acido Node是EF中的一个实体,可以是任何db表
  • 如果您使用 MSSql 然后使用内存中的 Sql Lite 进行测试,这将不起作用。
  • HasDefaultValueSql() 似乎不存在(你从哪里得到的?)并且第二个选项不起作用,生成的迁移不包括主键的默认 sql 设置应该的。
【解决方案3】:

在这里解决其他答案

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

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

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

EF 团队说明

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

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

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

我的回答(确实有效)

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

EF code first migrations, DB generated guid keys

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

像往常一样生成迁移脚本,像这样将两个属性放在关键属性上...

public class Foo
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public Guid Id { get; set; }
}

...从声明的角度来说,实体现在是正确的。

它将生成的迁移将类似于:

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

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

如果失败,则回滚迁移,然后将其修改为类似 ...

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

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

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

【讨论】:

  • 这应该被标记为答案。非常感谢!
  • 我认为关键问题在于许多不同的数据库提供者? 在一些严重的情况下它们的行为非常不同。
  • 提供者应该根据它正在与之交谈的特定类型的数据库端点来处理这个问题。我作为 EF 的消费者应该从中抽象出来,这就是我首先使用 EF 的原因。不要养狗,整天对着邻居的猫狂吠。
  • 这个解决方案的问题是你必须知道或记住每次创建新表时添加它。希望这能得到解决。
  • 完全同意,我讨厌这个解决方案,但找不到一种方法可以导致框架这样做,所以这感觉像是“所有糟糕选项中的最佳案例”。 EF 开发团队根本不会接受这是一个错误的想法。
【解决方案4】:

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

builder.Property(e => e.Id).ValueGeneratedOnAdd();

【讨论】:

  • @misha 您使用哪个数据库提供商?
  • 我认为您需要在属性之前指定实体。例如:builder.Entity().Property(...).ValueGeneratedOnAdd();
  • @PetrTomášek 这个构建器是从 IEntityTypeConfiguration.Confiure 方法内部调用的。这些在 DbContext.OnModelCreating 内部调用,有助于分解 OnModelCreating 方法。 docs.microsoft.com/en-us/dotnet/api/…
【解决方案5】:

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

【讨论】:

  • 请在修改后的问题中查看我的映射类,我将把 newsequentialid() 放在哪里?我试图先通过代码来做到这一点,只是认为 EF 会处理它?
  • 这显示了一个示例:blog.jongallant.com/2013/04/…
【解决方案6】:

您还可以在Sql Server 本身中将ID 的默认值设置为NewID(),并将GUID 传递为null

我以前在 SSMS 中这样做过。

【讨论】:

  • 顺序 ID 减少索引碎片,从而提高查询性能
  • 即使您在 SQL Server 中将 ID 的默认值设置为 NewID(),如果尚未设置 StoreGeneratedPattern 属性,您仍需要修改您的 .edmx 文件。参考:developerhandbook.com/entity-framework/…
  • @ChinhPhan 在标准情况下,我认为我们不需要更改任何 XML。我使用向导创建 EDMX 文件,一切正常。
【解决方案7】:

我更喜欢让数据库自动生成 id,例如以下架构:

CREATE TABLE [dbo].[MyTable](
    [MyId] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Booking_BookingId]  DEFAULT (newsequentialid())
    )

然后在代码优先映射中,我指定以下内容来告诉实体框架数据库将负责在插入时生成值。

Property(a => a.MyId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

【讨论】:

  • 首先使用代码,我们不会编写迁移执行的 SQL,并且您在此处显示的迁移代码不会导致您显示的 SQL,请参阅我的答案了解我的详细信息。跨度>
  • 注: “首先使用代码,我们不编写迁移执行的 SQL”不一定是真的。 “数据库中的代码优先”是我使用的,我确实编写了我的 SQL 模式,它是“代码优先”。命名可能会产生误导。
【解决方案8】:

实体框架核心更新

没有必要使用[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
不用fluent API

EF Core 自动处理它并为主键生成 Id

例子:

public class DummyEntity
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

}

播种

    _context.DummyEntities.Add(new DummyEntity
    {
        FirstName = "Abc",
        LastName = "Def",
        Postion = "User",
    });

    _context.SaveChanges();

【讨论】:

  • 在 EntityFrameworkCore 1.0.4 上,这还不够。我不得不使用 entity.Property(e => e.Id).ValueGeneratedOnAdd();这对我有用。
  • 投了反对票,因为这似乎并不准确。当我生成迁移时,defaultValue 设置为 new Guid("00000000-0000-0000-0000-000000000000"))。请更新您的答案。
  • 你是对的,ef core 负责处理它,但它没有构建一个数据库表来正确处理自动生成该 guid,例如在 SQL 服务器中它没有设置默认值或启用自动生成属性。
  • 这个答案既错误又非常糟糕。这有很多问题,我需要一个完整的论坛帖子来纠正其中的所有问题。
猜你喜欢
  • 1970-01-01
  • 2015-10-20
  • 2015-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-24
  • 2014-05-05
  • 1970-01-01
相关资源
最近更新 更多