简体   繁体   English

实体框架核心自动生成的 guid

[英]Entity Framework Core Auto Generated guid

Can some One guide me I want primeryKey of a table as guid having db generated value on insert.有人可以指导我primeryKey我想要一个表的primeryKey作为guid ,在插入时有db生成的值。

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

but it's giving error但它给出了error

The seed entity for entity type 'User' cannot be added because there was no value provided for the required property 'Id'.无法添加实体类型“用户”的种子实体,因为没有为所需的属性“Id”提供值。

Here is my actual model classes and DbContxt class:这是我的实际模型类和 DbContxt 类:

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

     [Required]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public DateTime CreatedOn { get; set; } = DateTime.UtcNow;


     [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
     public DateTime? UpdatedOn { get; set; }

     [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
     public DateTime LastAccessed { get; set; }
 }

 public class User : BaseModel
 {
        [Required]
        [MinLength(3)]
        public string Name { get; set; }

        [Required]
        [MinLength(3)]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        [MinLength(6)]
        public string Password { get; set; }
  }

Then in the MyDbContext:然后在 MyDbContext 中:

public class MyDbContext: DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder mb)
        {
            base.OnModelCreating(mb);
            
            mb.Entity<User>().HasData(
                new User() { Email = "Mubeen@gmail.com", Name = "Mubeen", Password = "123123" },
                new User() { Email = "Tahir@gmail.com", Name = "Tahir", Password = "321321" },
                new User() { Email = "Cheema@gmail.com", Name = "Cheema", Password = "123321" }
                );
        }

        public DbSet<User> User { get; set; }
    }

Any help please!请任何帮助!

The problem you are experiencing is not specific for autogenerated Guids.您遇到的问题并非特定于自动生成的 Guid。 The same happens for any autogenerated key values, including the commonly used auto increment (identity) columns.任何自动生成的键值都会发生同样的情况,包括常用的自动增量(标识)列。

It's caused by a specific Data Seeding ( HasData ) requirement:这是由特定的 数据播种( HasData ) 要求引起的:

This type of seed data is managed by migrations and the script to update the data that's already in the database needs to be generated without connecting to the database.这种类型的种子数据由迁移管理,需要在不连接数据库的情况下生成用于更新数据库中已有数据的脚本。 This imposes some restrictions:这施加了一些限制:

  • The primary key value needs to be specified even if it's usually generated by the database.即使通常由数据库生成,也需要指定主键值。 It will be used to detect data changes between migrations.它将用于检测迁移之间的数据更改。
  • Previously seeded data will be removed if the primary key is changed in any way.如果主键以任何方式更改,则先前播种的数据将被删除。

Note the first bullet.请注意第一个项目符号。 So while for normal CRUD your PK will be auto generated, you are required to specify it when using HasData fluent API, and the value must be constant (not changing), so you can't use Guid.NewGuid() .因此,对于普通的 CRUD,您的 PK 将自动生成,您需要在使用HasData fluent API 时指定它,并且该值必须是常量(不更改),因此您不能使用Guid.NewGuid() So you need to generate several Guids, take their string representation and use something like this:因此,您需要生成多个 Guid,获取它们的字符串表示形式并使用以下内容:

mb.Entity<User>().HasData(
    new User() { Id = new Guid("pre generated value 1"), ... },
    new User() { Id = new Guid("pre generated value 2"), ... },
    new User() { Id = new Guid("pre generated value 3"), ... }
    );

[DatabaseGenerated(DatabaseGeneratedOption.Identity)] on GUID field works on Entity Framework 6.x, may be not in EF Core yet! [DatabaseGenerated(DatabaseGeneratedOption.Identity)] GUID 字段适用于 Entity Framework 6.x,可能还没有在 EF Core 中!

So the solution is:所以解决办法是:

1) First write your BaseModel class as follows: 1)首先编写您的BaseModel类,如下所示:

public class BaseModel
{
    [Key]
    public Guid Id { get; set; }

    public DateTime CreatedOn { get; set; } = DateTime.UtcNow;

    public DateTime? UpdatedOn { get; set; }

    public DateTime LastAccessed { get; set; }
}

2) Then OnModelCreating() method in your DbContext should be as follows: 2) 那么你的DbContext OnModelCreating()方法应该如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      base.OnModelCreating(modelBuilder);

      modelBuilder.Entity<YourEntity>().Property(x => x.Id).HasDefaultValueSql("NEWID()");

      modelBuilder.Entity<User>().HasData(
            new User() { Id  = Guid.NewGuid(), Email = "Mubeen@gmail.com", Name = "Mubeen", Password = "123123" },
            new User() { Id = Guid.NewGuid(), Email = "Tahir@gmail.com", Name = "Tahir", Password = "321321" },
            new User() { Id = Guid.NewGuid(),  Email = "Cheema@gmail.com", Name = "Cheema", Password = "123321" }
            );
 }

Now create a brand new migration and update the database accordingly.现在创建一个全新的迁移并相应地更新数据库。 Hope your problem will be solved!希望你的问题能得到解决!

You can use defaultValueSql: "newid()" in your Code First Migration file.您可以在 Code First 迁移文件中使用defaultValueSql: "newid()"

For Example;例如;

 public override void Up()
    {
        CreateTable(
            "dbo.ExampleTable",
            c => new
            {
                Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"),               
                RowGuid = c.Guid(nullable: false, defaultValueSql: "newid()"),

            })
            .PrimaryKey(t => t.Id);           
    }

Rather than manually updating the migration, you could also implement an update in the OnModelCreating that adds it into the migration for you, eg除了手动更新迁移,您还可以在 OnModelCreating 中实现一个更新,为您将其添加到迁移中,例如

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // guid identities
    foreach (var entity in modelBuilder.Model.GetEntityTypes()
        .Where(t =>
            t.ClrType.GetProperties()
                .Any(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute)))))
    {
        foreach (var property in entity.ClrType.GetProperties()
            .Where(p => p.PropertyType == typeof(Guid) && p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute))))
        {
            modelBuilder
                .Entity(entity.ClrType)
                .Property(property.Name)
                .HasDefaultValueSql("newsequentialid()");
        }
    }

}  

GUID is handled by Identity framework and not by EF, so to use GUID you need to do as the following GUIDIdentity framework而不是 EF 处理,因此要使用 GUID,您需要执行以下操作

[Key]
public Guid Id { get; set; }

and then prepare the model before insert into table然后在插入表之前准备模型

new User() { Id = Guid.NewGuid(), Email = "Mubeen@gmail.com", Name = "Mubeen", Password = "123123" },
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
        {
            b.Property<string>("Id").HasMaxLength(36)
                .ValueGeneratedOnAdd();
});
  1. Create an extension method for easy use:创建一个易于使用的扩展方法:
namespace Microsoft.EntityFrameworkCore
{
    using System;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;
    using System.Reflection;
    public static class ModelBuilderExtensions
    {
        public static void SetDefaultValuesTableName(this ModelBuilder modelBuilder)
        {
            foreach (var entity in modelBuilder.Model.GetEntityTypes().Where(x => x.ClrType.GetCustomAttribute(typeof(TableAttribute)) != null))
            {
                var entityClass = entity.ClrType;

                foreach (var property in entityClass.GetProperties().Where(p => (p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(Guid)) && p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute))))
                {
                    var defaultValueSql = "GetDate()";
                    if (property.PropertyType == typeof(Guid))
                    {
                        defaultValueSql = "newsequentialid()";
                    }
                    modelBuilder.Entity(entityClass).Property(property.Name).HasDefaultValueSql(defaultValueSql);
                }
            }
        }
    }
}
  1. Modify your OnModelCreating method修改您的 OnModelCreating 方法
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.SetDefaultValuesTableName();
    }
  1. Profit利润

Source 来源

If all you need is getting an automatic value when creating and saving a new entity, you can also do it in the constructor:如果您只需要在创建和保存新实体时获取自动值,您也可以在构造函数中执行此操作:

public class BaseModel
{
   public BaseModel()
   {
      Id = new Guid();
   }

   [Key]
   public Guid Id { get; set; }

   public DateTime CreatedOn { get; set; } = DateTime.UtcNow;

   public DateTime? UpdatedOn { get; set; }

   public DateTime LastAccessed { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM