14

我正在玩EF Core 2.1 Preview 2。我在使用 HasData (Seed) 方法时遇到问题OnModelCreating(ModelBuilder modelBuilder)

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

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

在我的DbContext内部OnModelCreating方法中,数据库模型定义为

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”提供值

4

6 回答 6

12

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

我必须做的唯一更改是指定TenantID != 0.

modelBuilder.Entity<Tenant>().HasData(new []{ 
   new Tenant {
      TenantID = 1, // Must be != 0
      Name = "SystemTenant",
   }
});
于 2018-04-24T20:57:15.313 回答
3

在我对EF Core代码进行逆向工程并找到行代码之后,我创建了一个小“hack”来绕过 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"}
        //....
        );

    //....
}
于 2019-06-18T21:51:24.743 回答
2

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

builder.Entity<EntityName>().HasData(someSerive.GetById(id).AsEnumerable());

我必须将其更改为ToArray()

builder.Entity<EntityName>().HasData(someSerive.GetById(id).ToArray());

于 2019-03-11T09:26:45.913 回答
1

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

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" });
            });
于 2021-02-22T09:39:14.277 回答
0

模型种子数据的局限性

  • 用于测试的临时数据

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

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

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

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

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

我收到“无法添加实体类型'角色'的种子实体,因为属性'Id'需要一个非零值。考虑提供一个负值以避免与非种子数据发生冲突”播种 asp.net 身份时的错误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);
    }
}
于 2022-02-06T13:24:17.237 回答
0

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

于 2022-02-17T12:20:45.667 回答