实体框架6 GUID作为主键:不能将值NULL插入列'Id',表'FileStore'; 列不允许空值

Alg*_*das 71 c# entity-framework sql-server-2008 entity-framework-6

我有一个主键"Id"的实体,它是Guid:

public class FileStore
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

还有一些配置:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<FileStore>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    base.OnModelCreating(modelBuilder);
}
Run Code Online (Sandbox Code Playgroud)

当我尝试插入记录时,我收到以下错误:

无法将值NULL插入列'Id',表'FileStore'; 列不允许空值.INSERT失败.\ r \n语句已终止.

我不想手动生成Guid.我只想插入一条记录Id并由SQL Server生成.如果我设置.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity),则Id列不是SQL Server中的标识列.

如何配置实体框架以在SQL Server中自动生成Guid?

小智 92

除了将这些属性添加到您的Id列之外:

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

在您的迁移中,您应该更改您CreateTabledefaultValueSQL属性以添加到您的列,即:

Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),
Run Code Online (Sandbox Code Playgroud)

这将使您无需手动触摸数据库,正如您在评论中指出的那样,您希望使用Code First避免使用该数据库.

  • Azure笔记确实让我节省了一些时间.谢谢. (9认同)
  • 自Azure SQL V12 https://msdn.microsoft.com/en-us/library/ms189786.aspx以来,支持newsequentialid(). (3认同)

小智 16

试试这个 :

public class FileStore
 {
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public Guid Id { get; set; }
   public string Name { get; set; }
   public string Path { get; set; }
 }
Run Code Online (Sandbox Code Playgroud)

你可以查看这篇SO帖子.


Mur*_*ock 8

您可以将数据库中Id的默认值设置为newsequentialid()或newid().然后EF的身份配置应该工作.

  • 感谢您的回答,但我首先在项目中使用代码。所以我不想在创建数据库时做任何手动步骤。 (2认同)
  • @Algirdas为什么在每个实体的构造函数中都有一个Guid.NewGuid() (2认同)

Bla*_*ise 5

它发生在我之前.

当表已经创建并且我.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)稍后添加时,代码迁移在某种程度上无法为Guid列分配默认值.

修复:

我们所需要的只是转到数据库,选择Id列并newsequentialid()手动添加Default Value or Binding.

无需更新dbo .__ MigrationHistory表.

希望能帮助到你.


添加的解决方案New Guid()通常不优选,因为在理论上,你可能会得到一个意外重复的可能性.


您不必担心在数据库中直接编辑.所有实体框架都是自动化我们数据库工作的一部分.

翻译

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
Run Code Online (Sandbox Code Playgroud)

[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),
Run Code Online (Sandbox Code Playgroud)

如果我们的EF错过了一件事并且没有为我们添加默认值,那么请继续手动添加它.

  • 如果是“主键”,则该字段中不可能有重复的 guid 键。因为“主键”将具有唯一约束。数据库服务器将拒绝重复的主键。 (2认同)

All*_*lan 5

这适用于我(没有Azure),dev服务器上的SQL 2008 R2或本地工作站上的localdb\mssqllocaldb.注意:实体添加了Create,CreateBy,Modified,ModifiedBy和Version列.

public class Carrier : Entity
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个映射配置类

public class CarrierMap : EntityTypeConfiguration<Carrier>
{
    public CarrierMap()
    {
        HasKey(p => p.Id);

        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(p => p.Code)
            .HasMaxLength(4)
            .IsRequired()
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsClustered = true, IsUnique = true }));

        Property(p => p.Name).HasMaxLength(255).IsRequired();
        Property(p => p.Created).HasPrecision(7).IsRequired();
        Property(p => p.Modified)
            .HasColumnAnnotation("IX_Modified", new IndexAnnotation(new IndexAttribute()))
            .HasPrecision(7)
            .IsRequired();
        Property(p => p.CreatedBy).HasMaxLength(50).IsRequired();
        Property(p => p.ModifiedBy).HasMaxLength(50).IsRequired();
        Property(p => p.Version).IsRowVersion();
    }
}
Run Code Online (Sandbox Code Playgroud)

当您像这样执行add-migration时,这会在初始DbMigration中创建一个Up方法

        CreateTable(
            "scoFreightRate.Carrier",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Code = c.String(nullable: false, maxLength: 4),
                    Name = c.String(nullable: false, maxLength: 255),
                    Created = c.DateTimeOffset(nullable: false, precision: 7),
                    CreatedBy = c.String(nullable: false, maxLength: 50),
                    Modified = c.DateTimeOffset(nullable: false, precision: 7,
                        annotations: new Dictionary<string, AnnotationValues>
                        {
                            { 
                                "IX_Modified",
                                new AnnotationValues(oldValue: null, newValue: "IndexAnnotation: { }")
                            },
                        }),
                    ModifiedBy = c.String(nullable: false, maxLength: 50),
                    Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
                })
            .PrimaryKey(t => t.Id)
            .Index(t => t.Code, unique: true, clustered: true);
Run Code Online (Sandbox Code Playgroud)

注意:Id列没有获得默认值,请不要担心

现在执行Update-Database,你最终应该在数据库中得到一个表定义,如下所示:

CREATE TABLE [scoFreightRate].[Carrier] (
    [Id]         UNIQUEIDENTIFIER   DEFAULT (newsequentialid()) NOT NULL,
    [Code]       NVARCHAR (4)       NOT NULL,
    [Name]       NVARCHAR (255)     NOT NULL,
    [Created]    DATETIMEOFFSET (7) NOT NULL,
    [CreatedBy]  NVARCHAR (50)      NOT NULL,
    [Modified]   DATETIMEOFFSET (7) NOT NULL,
    [ModifiedBy] NVARCHAR (50)      NOT NULL,
    [Version]    ROWVERSION         NOT NULL,
    CONSTRAINT [PK_scoFreightRate.Carrier] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);


GO
CREATE UNIQUE CLUSTERED INDEX [IX_Code]
    ON [scoFreightRate].[Carrier]([Code] ASC);
Run Code Online (Sandbox Code Playgroud)

注意:我们重写了SqlServerMigrationSqlGenerator以确保它不会使主键成为聚簇索引,因为我们鼓励开发人员在表上设置更好的聚簇索引

public class OurMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation)
    {
        if (addPrimaryKeyOperation == null) throw new ArgumentNullException("addPrimaryKeyOperation");
        if (!addPrimaryKeyOperation.Table.Contains("__MigrationHistory"))
            addPrimaryKeyOperation.IsClustered = false;
        base.Generate(addPrimaryKeyOperation);
    }

    protected override void Generate(CreateTableOperation createTableOperation)
    {
        if (createTableOperation == null) throw new ArgumentNullException("createTableOperation");
        if (!createTableOperation.Name.Contains("__MigrationHistory"))
            createTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(createTableOperation);
    }

    protected override void Generate(MoveTableOperation moveTableOperation)
    {
        if (moveTableOperation == null) throw new ArgumentNullException("moveTableOperation");
        if (!moveTableOperation.CreateTableOperation.Name.Contains("__MigrationHistory")) moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(moveTableOperation);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

实体框架 \xe2\x80\x93 使用 Guid 作为主键

\n\n

使用实体框架时,使用 Guid 作为表主键比使用整数需要更多的工作。在您\xe2\x80\x99 阅读/被展示如何操作之后,设置过程非常简单。

\n\n

代码优先和数据库优先方法的过程略有不同。这篇文章讨论了这两种技术。

\n\n

在此输入图像描述

\n\n

代码优先

\n\n

采用代码优先方法时,使用 Guid 作为主键很简单。创建实体时,将 DatabaseGenerate 属性添加到主键属性中,如下所示;

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

实体框架将按照您的预期创建列,并具有主键和 uniqueidentifier 数据类型。

\n\n
codefirst-defaultvalue\n
Run Code Online (Sandbox Code Playgroud)\n\n

另请注意,非常重要的是,该列的默认值已设置为(newsequentialid())。这会为每一行生成一个新的顺序(连续)Guid。如果您愿意,可以将其更改为newid()),这将导致每个新行都有一个完全随机的 Guid。每次删除并重新创建数据库时,该信息都会被清除,因此在采用数据库优先方法时效果更好。

\n\n

数据库优先

\n\n

数据库优先方法与代码优先方法类似,但您\xe2\x80\x99 必须手动编辑模型才能使其工作。

\n\n

确保在执行任何操作之前编辑主键列并添加 (newsequentialid()) 或 (newid()) 函数作为默认值。

\n\n

在此输入图像描述

\n\n

接下来,打开 EDMX 图,选择适当的属性并打开属性窗口。确保 StoreGeneratePattern 设置为标识。

\n\n
databasefirst-model\n
Run Code Online (Sandbox Code Playgroud)\n\n

无需在代码中为您的实体提供 ID,该 ID 将在实体提交到数据库后自动为您填充;

\n\n
using (ApplicationDbContext context = new ApplicationDbContext())\n{\n    var person = new Person\n                     {\n                         FirstName = "Random",\n                         LastName = "Person";\n                     };\n\n    context.People.Add(person);\n    context.SaveChanges();\n    Console.WriteLine(person.Id);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

重要提示:您的 Guid 字段必须是主键,否则不起作用。实体框架会给你一个相当神秘的错误消息!

\n\n

概括

\n\n

Guid(全局唯一标识符)可以轻松地用作实体框架中的主键。为此需要付出一些额外的努力,具体取决于您采用的方法。使用代码优先方法时,请将 DatabaseGeneerated 属性添加到关键字段。采用“数据库优先”方法时,请在模型上将 StoredGeneratePattern 显式设置为 Identity。

\n\n
[1]: https://i.stack.imgur.com/IxGdd.png\n[2]: https://i.stack.imgur.com/Qssea.png\n
Run Code Online (Sandbox Code Playgroud)\n