EF Core 8初始迁移正常,后续创建迁移出现异常错误求助
我用EF Core 8完成了初始数据库迁移,但微调模型后创建新迁移时出错。为排查问题,我新建了独立项目逐个添加模型类,定位到了触发错误的代码。
正常运行的模型类结构
internal class Dictionary { public string Name { get; set; } public bool IsEnabled { get; set; } public ICollection<DictionaryEntry> Entries { get; set; } } internal class DictionaryEntry { public string Name { get; set; } public bool IsEnabled { get; set; } public Dictionary Dictionary { get; set; } } internal abstract class DictionaryEntry<TValue>: DictionaryEntry { public TValue Value { get; set; } } internal abstract class GroupedEntry<TValue, TEntryGroup>: DictionaryEntry<TValue> where TEntryGroup : DictionaryEntry { public virtual TEntryGroup EntryGroup { get; set; } } internal class EntryGroup<TEntry>: DictionaryEntry where TEntry : DictionaryEntry { public virtual ICollection<TEntry> Entries { get; set; } public virtual EntryGroup<TEntry> Parent { get; set; } public virtual ICollection<EntryGroup<TEntry>> Children { get; set; } } internal class EntryGroup<TGroupEntry, TEntry>: EntryGroup<TEntry> where TGroupEntry : DictionaryEntry where TEntry : DictionaryEntry { public virtual TGroupEntry GroupEntry { get; set; } = null!; } internal class EntryClassA: DictionaryEntry<uint> { public virtual EntryClassB LinkedDocumentation { get; set; } } internal class EntryClassB: GroupedEntry<uint, EntryGroup_EntryClassAB>; internal class EntryGroup_EntryClassAB: EntryGroup<EntryClassB, EntryClassA>
触发错误的新增类
internal abstract class Entry_ClassCBase: GroupedEntry<uint, EntryGroup_ClassC>; internal class Entry_ClassCGroup: Entry_ClassCBase; internal class Entry_ClassC: Entry_ClassCBase; internal class EntryGroup_ClassC: EntryGroup<Entry_ClassCGroup, Entry_ClassC>;
错误信息
Unable to create a 'DbContext' of type ''. The exception 'The dependent side could not be determined for the one-to-one relationship between 'EntryGroup_ClassC.GroupEntry' and 'Entry_ClassCGroup.EntryGroup'. To identify the dependent side of the relationship, configure the foreign key property. If these navigations should not be part of the same relationship, configure them independently via separate method chains in 'OnModelCreating'. See https://go.microsoft.com/fwlink/?LinkId=724062 for more details.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
注:主应用初始迁移时包含这些类并未报错,推测是后续代码调整导致问题。
DbContext代码
namespace Program { sealed class CultureInfoConverter: ValueConverter<CultureInfo, string> { public CultureInfoConverter() : base(static (v) => v.Name, static (v) => CultureInfo.GetCultureInfo(v)) { } } internal partial class Context(DbContextOptions<Context> options): DbContext(options) { public DbSet<Dictionary> Dictionaries { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { _ = modelBuilder. HasCollation("en_natural", locale: "en-u-kn-true", provider: "icu", deterministic: false); _ = modelBuilder.UseHiLo(null); _ = modelBuilder.Owned<Oid>(); _ = modelBuilder.Owned<Version>(); _ = modelBuilder.Entity<Dictionary>(). HasDiscriminator(); _ = modelBuilder.Entity<Dictionary>().Property<uint>("RowVersion"). IsRowVersion(); _ = modelBuilder.Entity<DictionaryEntry>(). HasDiscriminator(); _ = modelBuilder.Entity<DictionaryEntry>().Property<uint>("RowVersion"). IsRowVersion(); _ = modelBuilder.Entity<Dictionary>().Property<ulong>("Id"); _ = modelBuilder.Entity<DictionaryEntry>().Property<ulong>("Id"); } protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { _ = configurationBuilder.Properties<string>(). UseCollation("en_natural"). AreUnicode(); _ = configurationBuilder.Properties<CultureInfo>(). HaveConversion<CultureInfoConverter>(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => _ = optionsBuilder.UseExceptionProcessor(); } }
问题原因与解决办法
EF Core无法自动推断EntryGroup_ClassC.GroupEntry和Entry_ClassCGroup.EntryGroup之间一对一关系的依赖方,核心原因是两个实体都继承自抽象类,EF无法通过默认规则判断哪一方是从属实体。
具体解决步骤:
显式配置一对一关系
首先在Entry_ClassCGroup中添加外键属性:internal class Entry_ClassCGroup: Entry_ClassCBase { public ulong EntryGroupId { get; set; } // 类型匹配EntryGroup_ClassC的Id类型 }然后在
OnModelCreating中通过Fluent API指定关系的依赖方和外键:modelBuilder.Entity<Entry_ClassCGroup>() .HasOne(e => e.EntryGroup) .WithOne(g => g.GroupEntry) .HasForeignKey<Entry_ClassCGroup>(nameof(Entry_ClassCGroup.EntryGroupId)) .IsRequired(false); // 根据业务需求设置是否必填调整关系类型(如果业务允许)
如果实际业务场景允许一个EntryGroup_ClassC对应多个Entry_ClassCGroup,可以将其中一方的导航属性改为集合类型,EF会自动识别为一对多关系,无需额外配置。回溯代码差异
主应用初始迁移正常,大概率是当时的代码中存在EF可识别的外键属性或不同的继承结构,后续调整中这些条件被移除,导致EF无法自动推断关系。可以对比初始迁移时的代码版本,定位差异点。
内容的提问来源于stack exchange,提问作者ZzZombo

