EF6 Table 拆分和异常 "A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'."
EF6 Table Splitting and exception "A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'."
一段时间以来,我一直在遵循 Entity Framework 的 Object Calisthenics in my domain design. In order to avoid creating a useless "Collection" table though, I use the table splitting 配置中的 "First Class Collections" 规则。
但是如果出于某种原因我有一个 Parent
class 除了其 Id 和子集合之外没有其他属性,我会得到异常:
InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'.
奇怪的是数据库创建正确,我可以从中查询,但无法保存。
如果我简单地在Parent
中再添加一个属性,问题就消失了,这就更奇怪了。
我将其缩小为一个非常简单的测试用例:
Program.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
}
}
配置顺序很重要,父配置必须在前。
直接从 DbContext class 配置时,保持从父到子的配置逻辑顺序是很自然的,但是当您 separate the config using EntityTypeConfiguration<TEntity>
时,情况可能并非如此。
对于这种特定情况,这似乎是 Entity Framework 中的一个错误,因为它在大多数情况下都有效。
为确保它始终有效,只需先调用层次结构中较高 classes 的配置。
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);
}
...
}
一段时间以来,我一直在遵循 Entity Framework 的 Object Calisthenics in my domain design. In order to avoid creating a useless "Collection" table though, I use the table splitting 配置中的 "First Class Collections" 规则。
但是如果出于某种原因我有一个 Parent
class 除了其 Id 和子集合之外没有其他属性,我会得到异常:
InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'.
奇怪的是数据库创建正确,我可以从中查询,但无法保存。
如果我简单地在Parent
中再添加一个属性,问题就消失了,这就更奇怪了。
我将其缩小为一个非常简单的测试用例:
Program.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
}
}
配置顺序很重要,父配置必须在前。
直接从 DbContext class 配置时,保持从父到子的配置逻辑顺序是很自然的,但是当您 separate the config using EntityTypeConfiguration<TEntity>
时,情况可能并非如此。
对于这种特定情况,这似乎是 Entity Framework 中的一个错误,因为它在大多数情况下都有效。
为确保它始终有效,只需先调用层次结构中较高 classes 的配置。
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);
}
...
}