0

一段时间以来,我在我的领域设计中一直遵循Object Calisthenics中的“First Class Collections”规则。不过,为了避免创建无用的“集合”表,我使用了实体框架中的表拆分配置。

但是,如果出于某种原因,我有一个Parent除了其 Id 和子集合之外没有属性的类,我会得到异常:

InvalidOperationException:ReferentialConstraint 中的依赖属性映射到存储生成的列。列:'Id'。

奇怪的是数据库创建正确,我可以从中查询,但无法保存。

如果我只是简单地在 中添加另一个属性Parent,问题就消失了,这就更奇怪了。

我把它缩小到一个非常简单的测试用例:

程序.cs

class Program
{
    static void Main(string[] args)
    {
        using (var context = new MyContext(new DropCreateDatabaseAlways<MyContext>()))
        {
            context.Set<Parent>().Find(1);
        }

        using (var context = new MyContext(new CreateDatabaseIfNotExists<MyContext>()))
        {
            context.Set<Parent>().Add(
                new Parent()
                {
                    ChildrenCollection = new ChildrenCollection()
                    {
                        List = new List<Child>() { new Child() }
                    }
                });
            context.SaveChanges(); // Exception thrown here
        }
    }
}

领域

public class Parent
{
    public int Id { get; set; }
    public virtual ChildrenCollection ChildrenCollection { get; set; }
}

public class ChildrenCollection
{
    public int Id { get; set; }
    public virtual IList<Child> List { get; set; }
}

public class Child
{
    public int Id { get; set; }
}

语境

public class MyContext : DbContext
{
    public MyContext(IDatabaseInitializer<MyContext> dbInitializer)
        : base(nameOrConnectionString: GetConnectionString())
    {
        Database.SetInitializer(dbInitializer);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ChildrenCollectionConfiguration());
        modelBuilder.Configurations.Add(new ParentConfiguration());

        base.OnModelCreating(modelBuilder);
    }

    private static string GetConnectionString()
    {
        return @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TestEntityFramework;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=true;";
    }
}

配置

public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
    public ParentConfiguration()
    {
        HasRequired(x => x.ChildrenCollection)
            .WithRequiredPrincipal();
    }
}

public class ChildrenCollectionConfiguration : EntityTypeConfiguration<ChildrenCollection>
{
    public ChildrenCollectionConfiguration()
    {
        #region Configure Table Splitting

        var parentTable = typeof(Parent).Name;

        ToTable(parentTable);

        HasMany(x => x.List)
            .WithRequired()
            .Map(x =>
            {
                x.MapKey(string.Concat(parentTable, "_Id"));
            });

        #endregion
    }
}
4

1 回答 1

0

配置顺序很重要,必须先配置父配置。

直接从 DbContext 类进行配置时,保持从 Parent 到 Child 的配置逻辑顺序是很自然的,但是当您使用 分隔配置EntityTypeConfiguration<TEntity>时,情况可能并非如此。

对于这种特定场景,这似乎是实体框架中的一个错误,因为它在大多数情况下都有效。

为确保它始终有效,只需首先调用层次结构中较高类的配置。

public class MyContext : DbContext
{
    ...
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ParentConfiguration()); // must come first
        modelBuilder.Configurations.Add(new ChildrenCollectionConfiguration()); // comes after

        base.OnModelCreating(modelBuilder);
    }
    ...
}
于 2020-05-02T19:55:17.120 回答