首页 文章

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

提问于
浏览
68

我有一个主键“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插入列'Id',表'FileStore';列不允许空值 . INSERT失败 . \ r \ n语句已终止 .

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

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

10 回答

  • 4

    如果您执行Code-First并且已经拥有数据库:

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

    你不能 . 你会/做很多事情 . 喜欢关系 . 哪个依赖于被拉回的数字,EF无法按照您设置的方式进行 . 打破每种模式的价格都有 .

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

  • 5

    这是什么东西?

    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; }
    }
    
  • 16

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

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

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

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

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

  • 90

    试试这个 :

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

    你可以查看SO post .

  • 5

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

  • 1

    它发生在我之前 .

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

    修复:

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

    无需更新dbo .__ MigrationHistory表 .

    希望能帮助到你 .


    添加 New Guid() 的解决方案通常不是首选,因为从理论上讲,您可能会意外地获得重复 .


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

    翻译

    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
    

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

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

  • 4

    这适用于我(没有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; }
    }
    

    然后创建一个映射配置类

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

    当您像这样执行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);
    

    Note: that the Id columns does not get a default value, don't worry

    现在执行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);
    

    Note: we have a overridden the SqlServerMigrationSqlGenerator to ensure it does NOT make the Primary Key a Clustered index as we encourage our developers to set a better clustered index on tables

    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);
        }
    }
    
  • 0

    根据this,如果已经创建了表,则特定迁移不会检测到DatabaseGeneratedOption.Identity,这是我遇到的情况 . 所以我删除了数据库和特定的迁移并添加了新的迁移,最后更新了数据库,然后一切都按预期工作 . 我使用的是EF 6.1,SQL2014和VS2013 .

  • 2

    Entity Framework – Use a Guid as the primary key

    使用Guid作为表的主键,在使用Entity Framework时,需要比使用整数时更省力 . 在您阅读/显示了如何操作之后,设置过程非常简单 .

    Code First和Database First方法的过程略有不同 . 本文讨论了这两种技术 .

    enter image description here

    Code First

    在采用代码第一种方法时使用Guid作为主键很简单 . 创建实体时,将DatabaseGenerated属性添加到主键属性中,如下所示;

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

    实体框架将使用主键和uniqueidentifier数据类型按预期创建列 .

    codefirst-defaultvalue
    

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

    Database First

    数据库第一种方法遵循与代码第一种方法类似的方法,但您必须手动编辑模型才能使其正常工作 .

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

    enter image description here

    接下来,打开你的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字段必须是主键,否则这不起作用 . 实体框架会给你一个相当神秘的错误信息!

    Summary

    Guid(全球唯一标识符)可以轻松地用作实体框架中的主键 . 根据您采取的方法,需要做一些额外的努力才能做到这一点 . 使用代码优先方法时,将DatabaseGenerated属性添加到关键字段 . 采用Database First方法时,在模型上明确将StoredGeneratedPattern设置为Identity .

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

相关问题