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


73

我有一个主键为“ Id”的实体,该实体为Guid:

public class FileStore
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}

和一些配置:

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

当我尝试插入记录时,出现以下错误:

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

我不想手动生成Guid。我只想插入一条记录Id并由SQL Server生成。如果我设置.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)Id列不是SQL Server中的“身份”列。

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


您是否尝试过在[DatabaseGenerated(DatabaseGeneratedOption.Identity)]前面添加注释public Guid ID {get; set;}

在表的初始构建之后是否添加了配置?
user3411327 2014年

Inanikian,我认为流利的api是首选,因为OnModelCreating在此将其覆盖。
布莱斯

1
我看到您尚未接受任何答案。您不满意吗?如果是这样,请让我知道,我会再发布一个,这样就可以了。只是觉得有点懒惰,如果我没得到代表,就不想打字。:)
Konrad Viltersten '16

Answers:


97

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

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

在迁移中,您应该更改CreateTable以将defaultValueSQL属性添加到列中,即:

Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),

这将避免您不得不手动触摸数据库,正如您在注释中指出的那样,您要使用Code First避免这种情况。


10
那条Azure笔记肯定为我节省了一些时间。谢谢。
Stanley.Goldman,2015年

3
从Azure SQL V12 msdn.microsoft.com/en-us/library/ms189786.aspx开始支持newsequentialid()。
DalSoft

1
这是什么,在什么地方使用它?“ Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),
deadManN

我知道已经有一段时间了,但是对于未来的读者来说,Id赋值语句放在codefirst迁移文件中。
Byrel Mitchell '18

18

尝试这个 :

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

您可以检查此SO帖子


1
但是我认为通过覆盖OnModelCreating可以像使用此属性一样设置相同的设置。我将阅读该帖子。谢谢
Algirdas 2014年

9

您可以将数据库中ID的默认值设置为newsequentialid()或newid()。然后,EF的身份配置应该起作用。


1
感谢您的回答,但我首先在项目上使用代码。因此,我不想在创建数据库上执行任何手动步骤。
Algirdas 2014年

2
@Algirdas为什么现在在每个实体的构造函数中都有一个Guid.NewGuid()
默多克

我没有找到其他解决方案,所以我将Guid.NewGuid()添加到构造函数中。谢谢
Algirdas 2014年

1
@Algirdas Just仅供参考,Guid.NewGuid()不会在数据库中建立索引。您应该使用顺序的Guid。
Josh Mouch 2015年

6

这适用于我(无Azure),开发服务器上的SQL 2008 R2或本地工作站上的localdb \ mssqllocaldb。注意:实体会添加“创建”,“创建”,“修改”,“修改”和“版本”列。

public class Carrier : Entity
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

然后创建一个映射配置类

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();
    }
}

当您像这样执行添加迁移时,这将在初始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);

注意: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);

注意:我们已经重写了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);
    }
}

5

以前发生在我身上。

创建表并在.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)以后添加时,代码迁移无法以某种方式为Guid列分配默认值。

解决方法:

我们所需要的只是进入数据库,选择Id列,然后newsequentialid()手动将其添加到中Default Value or Binding

无需更新dbo .__ MigrationHistory表。

希望能帮助到你。


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


而且,您不必担心直接在数据库中进行编辑。所有Entity Framework所做的工作都是自动化我们数据库工作的一部分。

正在翻译

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)

进入

[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),

如果某种原因我们的EF遗漏了一件事并且没有为我们添加默认值,则继续手动添加它。


1
如果为Primary Key,则该字段中不可能有重复的guid键。因为Primary Key会有一个独特的约束。数据库服务器将拒绝重复的主键。
AechoLiu 2014年

4

根据这个,DatabaseGeneratedOption.Identity不是由特定迁移,如果它检测到添加的表已经建立,这是我碰到的情况。因此,我删除了该数据库并进行了特定的迁移,并添加了一个新的迁移,最后更新了数据库,然后一切正常。我正在使用EF 6.1,SQL2014和VS2013。


仅供参考:在ef7 RC1中工作[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]很好...但是也仅在新数据库中进行了测试...
Miroslav Siska

4

实体框架–使用Guid作为主键

使用Entity Framework时,将Guid用作表的主键比使用整数需要更多的工作。在您阅读/了解如何操作后,设置过程非常简单。

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

在此处输入图片说明

代码优先

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

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

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

codefirst-defaultvalue

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

数据库优先

数据库优先方法与代码优先方法类似,但是您必须手动编辑模型以使其起作用。

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

在此处输入图片说明

接下来,打开EDMX图,选择适当的属性,然后打开属性窗口。确保将StoreGeneratedPattern设置为identity。

databasefirst-model

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

using (ApplicationDbContext context = new ApplicationDbContext())
{
    var person = new Person
                     {
                         FirstName = "Random",
                         LastName = "Person";
                     };

    context.People.Add(person);
    context.SaveChanges();
    Console.WriteLine(person.Id);
}

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

概要

Guid(全局唯一标识符)可以轻松用作Entity Framework中的主键。为此,需要花费一些额外的精力,具体取决于您采用的方法。使用代码优先方法时,将DatabaseGenerated属性添加到您的关键字段。采用数据库优先方法时,请在模型上将StoredGeneratedPattern显式设置为Identity。

[1]: https://i.stack.imgur.com/IxGdd.png
[2]: https://i.stack.imgur.com/Qssea.png

2

如果您执行代码优先并且已经有一个数据库:

public override void Up()
{
    AlterColumn("dbo.MyTable","Id", c =>  c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}

1

你不能。您会破坏很多事情。喜欢关系。这取决于EF无法以您设置方式退回的号码。打破每种模式的代价。

在C#层中生成GUID,以便关系可以继续工作。


0

什么样的东西呢?

public class Carrier : Entity
{
    public Carrier()
    {
         this.Id = Guid.NewGuid();
    }  
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

2
这样会产生碎片,对于小型数据库而言这不是问题,但大型数据库应使用顺序引导
Calimero100582'1

1
@Miroslav,我假设您在谈论上面的答案,如果是这样,请不要添加构造函数,当实体保存在数据库中时,数据库将创建一个顺序的guid。
艾伦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.