EF Core 8种子数据报错:无法创建DbContext类型实例
EF Core 种子数据添加时导航属性异常排查与解决
问题场景
执行迁移命令:
dotnet ef migrations add InitDb -s .\DemoProject.API\ -p .\DemoProject.EntityFramework\
触发以下错误:
无法创建''类型的'DbContext'。创建实例时抛出异常:'Character实体类型的种子实体无法添加,因为它已设置导航属性"Player"。要播种关系,请将实体种子添加到"Character"并指定外键值{'PlayerId'}。
考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看相关属性值。'
已在DataSeed.cs中为Character实体指定外键PlayerId,但问题仍存在。
相关代码
Character.cs
public class Character { public Character() { DateCreated = DateOnly.FromDateTime(DateTime.Now); } public Guid Id { get; set; } public string NickName { get; set; } = string.Empty; public string Classes { get; set; } public int Level { get; set; } public DateOnly DateCreated { get; set; } public Guid PlayerId { get; set; } public Player Player { get; set; } }
CharacterMapping.cs
public class CharacterMapping : IEntityTypeConfiguration<Character> { public void Configure(EntityTypeBuilder<Character> builder) { builder.Property(x => x.NickName).HasMaxLength(20); builder.Property(x => x.Classes).HasMaxLength(20); builder.HasIndex(x => x.NickName).IsUnique(); builder.HasOne(c => c.Player) .WithMany(p => p.Characters) .HasForeignKey(c => c.PlayerId); } }
Player.cs
public class Player { public Player() { DateCreated = DateOnly.FromDateTime(DateTime.Now); } public Guid Id { get; set; } public string Account { get; set; } public string AccountType { get; set; } public DateOnly DateCreated { get; set; } public ICollection<Character> Characters { get; set; } }
PlayerMapping.cs
public class PlayerMapping:IEntityTypeConfiguration<Player> { public void Configure(EntityTypeBuilder<Player> builder) { builder.Property(x => x.Account).HasMaxLength(50); builder.Property(x => x.AccountType).HasMaxLength(10); builder.HasIndex(x => x.Account).IsUnique(); } }
AppDbContext.cs
public class AppDbContext:DbContext { public AppDbContext(DbContextOptions options):base(options) { } public DbSet<Player> Players { get; set; } public DbSet<Character> Characters { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new CharacterMapping()); modelBuilder.ApplyConfiguration(new PlayerMapping()); modelBuilder.Entity<Player>() .HasData(DataSeed.Players); modelBuilder.Entity<Character>() .HasData(DataSeed.Characters); } }
DataSeed.cs
public static class DataSeed { public static List<Player> Players { get; } = [ new Player { Id = Guid.NewGuid(), Account = "player1@example.com", AccountType = "Standard" }, new Player { Id = Guid.NewGuid(), Account = "player2@example.com", AccountType = "Premium" }, new Player { Id = Guid.NewGuid(), Account = "player3@example.com", AccountType = "Standard" } ]; public static Character[] Characters { get; } = [ new Character { Id = Guid.NewGuid(), NickName = "Character1", Classes = "Warrior", Level = 10, PlayerId = Players[0].Id }, new Character { Id = Guid.NewGuid(), NickName = "Character2", Classes = "Mage", Level = 8, PlayerId = Players[0].Id }, new Character { Id = Guid.NewGuid(), NickName = "Character3", Classes = "Ranger", Level = 12, PlayerId = Players[1].Id }, new Character { Id = Guid.NewGuid(), NickName = "Character4", Classes = "Paladin", Level = 7, PlayerId = Players[2].Id }, new Character { Id = Guid.NewGuid(), NickName = "Character5", Classes = "Druid", Level = 5, PlayerId = Players[2].Id } ]; }
问题根源
EF Core 种子数据要求实体的主键必须是固定不变的值,而你在DataSeed.cs中使用Guid.NewGuid()动态生成主键和外键。每次运行程序(包括生成迁移)时,这些Guid会重新生成,导致EF Core无法匹配Character的外键PlayerId与Player的主键Id之间的对应关系,进而误判为导航属性被非法设置。
解决步骤
替换动态Guid为固定值:手动生成固定的Guid作为Player和Character的主键,确保每次程序运行时这些值都一致。
修改后的DataSeed.cs示例:public static class DataSeed { // 固定Player主键 private static readonly Guid Player1Id = new Guid("11111111-1111-1111-1111-111111111111"); private static readonly Guid Player2Id = new Guid("22222222-2222-2222-2222-222222222222"); private static readonly Guid Player3Id = new Guid("33333333-3333-3333-3333-333333333333"); public static List<Player> Players { get; } = [ new Player { Id = Player1Id, Account = "player1@example.com", AccountType = "Standard" }, new Player { Id = Player2Id, Account = "player2@example.com", AccountType = "Premium" }, new Player { Id = Player3Id, Account = "player3@example.com", AccountType = "Standard" } ]; public static Character[] Characters { get; } = [ new Character { Id = new Guid("10000000-0000-0000-0000-000000000000"), NickName = "Character1", Classes = "Warrior", Level = 10, PlayerId = Player1Id }, new Character { Id = new Guid("10000000-0000-0000-0000-000000000001"), NickName = "Character2", Classes = "Mage", Level = 8, PlayerId = Player1Id }, new Character { Id = new Guid("10000000-0000-0000-0000-000000000002"), NickName = "Character3", Classes = "Ranger", Level = 12, PlayerId = Player2Id }, new Character { Id = new Guid("10000000-0000-0000-0000-000000000003"), NickName = "Character4", Classes = "Paladin", Level = 7, PlayerId = Player3Id }, new Character { Id = new Guid("10000000-0000-0000-0000-000000000004"), NickName = "Character5", Classes = "Druid", Level = 5, PlayerId = Player3Id } ]; }初始化Player的Characters集合(可选但推荐):在Player的构造函数中初始化Characters集合,避免空引用问题:
public Player() { DateCreated = DateOnly.FromDateTime(DateTime.Now); Characters = new List<Character>(); }重新生成迁移:删除之前生成的迁移文件(如果有的话),重新执行迁移命令即可。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

