For some time I've been following the "First Class Collections" rule from Object Calisthenics in my domain design. In order to avoid creating a useless "Collection" table though, I use the table splitting configuration from Entity Framework.
But if for whatever reason I have a Parent
class that doesn't have properties besides its Id and the child collection, I get the exception:
InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'.
The weird thing is that the database is created correctly, and I can query from it, but saving isn't possible.
If I simply add another property to the Parent
, the problem disappears, which is even more strange.
I narrowed it down to a very simple test case:
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
}
}
}
Domain
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; }
}
Context
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;";
}
}
Configuration
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
}
}