【问题标题】:The seed entity for entity type 'X' cannot be added because the was no value provided for the required property "..ID"无法添加实体类型“X”的种子实体,因为没有为所需属性“..ID”提供值
【发布时间】:2022-02-17 20:20:45
【问题描述】:

我在玩 EF Core 2.1 Preview 2。 我在 OnModelCreating(ModelBuilder modelBuilder) 中使用 HasData (Seed) 方法有问题

我的模型是没有注释的简单 POCO 类。

public class Tenant {
    public int TenantID {get; set;}
    public string Name {get; set;}
}

在我的DbContext 里面的OnModelCreating 方法中,DB 模型定义为

modelBuilder.Entity<Tenant>(e => {
    e.HasKey(m => m.TenantID)
     .HasName("PK_Tenants");

    e.Property(m => m.TenantID)
     .UseSqlServerIdentityColumn();

    e.Property(m => m.Name)
     .IsRequired()
     .HasMaxLength(256);
}

种子方法定义为:

modelBuilder.Entity<Tenant>().HasData(new []{
   new Tenant {
      TenantID = 0,
      Name = "SystemTenant",
   }
});

在启动期间,当 ctx.Database.Migrate() 运行时,出现异常: 无法添加实体类型“租户”的种子实体,因为没有为所需的属性“租户 ID”提供值

【问题讨论】:

    标签: c# entity-framework-core entity-framework-core-2.1


    【解决方案1】:

    这个例外有点误导。内部必须有某种机制来测试所需的属性,因此它们必须不同于默认值。

    我要做的唯一更改是指定TenantID != 0

    modelBuilder.Entity<Tenant>().HasData(new []{ 
       new Tenant {
          TenantID = 1, // Must be != 0
          Name = "SystemTenant",
       }
    });
    

    【讨论】:

      【解决方案2】:

      在我对 EF Core 代码进行逆向工程并找到 this 代码行之后,我创建了一个小“黑客”来绕过 0 PK 值限制。 与@Tomino 答案相关。 这是我截断的扩展代码:

      using System;
      using System.Linq;
      using System.Collections.Generic;
      
      using Microsoft.EntityFrameworkCore;
      using Microsoft.EntityFrameworkCore.Internal;
      using Microsoft.EntityFrameworkCore.Metadata.Internal;
      
      namespace EntityFrameworkCore.CustomMigration
      {
          public static class CustomModelBuilder
          {
              public static bool IsSignedInteger(this Type type)
                 => type == typeof(int)
                    || type == typeof(long)
                    || type == typeof(short)
                    || type == typeof(sbyte);
      
              public static void Seed<T>(this ModelBuilder modelBuilder, IEnumerable<T> data) where T : class
              {
                  var entnty = modelBuilder.Entity<T>();
      
                  var pk = entnty.Metadata
                      .GetProperties()
                      .FirstOrDefault(property => 
                          property.RequiresValueGenerator() 
                          && property.IsPrimaryKey()
                          && property.ClrType.IsSignedInteger()
                          && property.ClrType.IsDefaultValue(0)
                      );
                  if (pk != null)
                  {
                      entnty.Property(pk.Name).ValueGeneratedNever();
                      entnty.HasData(data);
                      entnty.Property(pk.Name).UseSqlServerIdentityColumn();
                  }
                  else
                  {
                      entnty.HasData(data);
                  }          
              }
          }
      }
      

      你可以在OnModelCreating方法中这样使用它:

      protected override void OnModelCreating(ModelBuilder builder)
      {
          base.OnModelCreating(builder);
      
          builder.Seed(new List<Tenant> {
              new Tenant() {TenantID = 0 , Name = string.Empty},
              new Tenant() {TenantID = 1 , Name = "test"}
              //....
              );
      
          //....
      }
      

      【讨论】:

        【解决方案3】:

        我使用自定义服务将数据提取到数据库,我是这样做的:

        builder.Entity&lt;EntityName&gt;().HasData(someSerive.GetById(id).AsEnumerable());

        我得把它改成ToArray():

        builder.Entity&lt;EntityName&gt;().HasData(someSerive.GetById(id).ToArray());

        【讨论】:

          【解决方案4】:

          我在我的一个存储库中遇到了这个错误。

          Exception has occurred: CLR/System.InvalidOperationException
          Exception thrown: 'System.InvalidOperationException' in Microsoft.EntityFrameworkCore.dll: 
          'The seed entity for entity type 'SubscriptionType'
          cannot be added because a default value was provided for the required property 'Id'. 
          
          Please provide a value different from '00000000-0000-0000-0000-000000000000'.'
                 at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateData(IModel model, IDiagnosticsLogger`1 logger)
                 at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
                 at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
                 at Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
          

          问题是我使用new Guid() 将值分配给id 字段,这只是一个错误。 new Guid 只会创建全零的虚拟 Guid,而 Guid.NewGuid() 将创建唯一的 Guid。所以,我不得不在我的代码中使用Guid.NewGuid()

          builder.Entity<SubscriptionType>(b =>
                      {
                          b.HasKey(k => k.Id);
                          b.HasData(new SubscriptionType { Id = Guid.NewGuid(), Name = "SeatBased" },
                                         new SubscriptionType { Id = Guid.NewGuid(), Name = "NonSeatBased" },
                                         new SubscriptionType { Id = Guid.NewGuid(), Name = "AzureEntitlement" },
                                         new SubscriptionType { Id = Guid.NewGuid(), Name = "Bundle" });
                      });
          

          【讨论】:

            【解决方案5】:

            限制模型种子数据:

            • 用于测试的临时数据

            • 依赖于数据库状态的数据

            • 大数据(种子数据在迁移快照中被捕获,大数据会很快导致大文件和性能下降)。

            • 需要由数据库生成键值的数据,包括使用备用键作为身份的实体

            • 需要自定义转换的数据(不通过值转换处理),例如一些密码散列

            • 需要调用外部 API 的数据,例如 ASP.NET Core 身份角色和用户创建

            我收到“无法添加实体类型 'Role' 的种子实体,因为属性 'Id' 需要一个非零值。考虑提供一个负值以避免与非种子数据发生冲突”在播种 asp 时出现错误.net Identity Role ,所以我们应该设置Id值:

            namespace Attendance.Server.Data.Configs; 
            
            public class RoleConfig : IEntityTypeConfiguration<Role>
                {
                    public void Configure(EntityTypeBuilder<Role> builder)
                    {
                        builder.ToTable("Role");
                        builder.Property(p => p.Name).IsUnicode(false).HasMaxLength(50).IsRequired();
                        builder.Property(p => p.NormalizedName).IsUnicode(false).HasMaxLength(50).IsRequired();
                        builder.Property(p => p.ConcurrencyStamp).IsUnicode(false);
                
                        // seed
                        builder.HasData(
                            new Role { Id = 1, Name = "admin", NormalizedName = "ADMIN" },
                            new Role { Id = 2, Name = "user", NormalizedName = "USER" }
                        );
                    }
                }
            

            我的角色实体

            using Microsoft.AspNetCore.Identity;
            
            namespace Attendance.Server.Data.Entities;
            
            public partial class Role : IdentityRole<int>
            {
            
            }
            

            使用 DbContext 中的 ApplyConfigurationsFromAssembly 方法,只需一行代码即可添加所有配置类:

            namespace Attendance.Server.Data;
            
            public class AppDbContext : IdentityDbContext<User, Role, int, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>, IUnitOfWork
            {
                public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
            
                public Task<int> SaveChangesAsync()
                {
                    return base.SaveChangesAsync();
                }
            
                public void MarkAsDeleted<TEntity>(TEntity entity) => base.Entry(entity).State = EntityState.Deleted;
            
                protected override void OnModelCreating(ModelBuilder modelBuilder)
                {
                    base.OnModelCreating(modelBuilder);
                    modelBuilder.ApplyConfigurationsFromAssembly(typeof(UserConfig).Assembly);
                }
            }
            

            【讨论】:

              【解决方案6】:

              我遇到了这个问题,我在我的模型类中添加了一个新属性并尝试迁移,我将 [Required] 属性保留到我的属性中,并忘记在我的种子数据中包含该属性。请在protected override void OnModelCreating(ModelBuilder modelBuilder) 方法中检查您的模型类和种子数据。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-06-24
                • 1970-01-01
                • 1970-01-01
                • 2020-03-26
                • 1970-01-01
                • 1970-01-01
                • 2021-08-27
                • 1970-01-01
                相关资源
                最近更新 更多