Entity Framework:TPC MapInheritedProperties 模型超 class 属性
Entity Framework: TPC MapInheritedProperties model super class properties
总结:在Entity Framework中,我使用TPC创建了两个源自相同基础class的classes。在流利的 API 中,我映射了继承的属性,但是如何对基础属性进行建模 class?
更广泛的描述
在Entity Framework我有一个classChild,还有两种Children:男孩和女孩。 Boy 和 Girl 都派生自 Child:
public class Child
{
public int Id {get; set;}
public string Name {get; set;}
}
public class Boy : Child
{
public string SomeBoyishProperty {get; set;}
}
public class Girl : Child
{
public string SomeGirlyProperty {get; set;}
}
我想要一个 table 男孩和一个 table 女孩,每个 table 也有 Child 属性。
public class MyDbContext : DbContext
{
public DbSet<Boy> Boys {get; set;}
public DbSet<Girl> Girls {get; set;
}
From several sources, for example this one 我了解到这称为 TPC:table 每个具体 class 并且我应该在 OnModelCreating
中使用 MapInheritedProperties
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// model the properties of the base class, for instance set max length
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
// Model Daughter:
modelBuilder.Entity<Daughter>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
})
.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
modelBuilder.Entity<Son>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
})
.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
在 SaveChanges 期间,我收到一个 InvlidOperationException 指示主键不是唯一的。删除构建 Child 的部分解决了这个问题。
如何构建 Child 属性而不必在 Girl 和 Boy 属性中再次执行此操作?
简答:
如果您希望代码正常工作,请删除模型配置中对 Child
实体的所有引用。一旦 EF 知道 Child
作为实体,它将强制执行以下规则:不能有 2 个类型为 Child
的实体或 2 个从 Child
继承的实体在内存中具有相同的 PK。您可以看到错误告诉您成功保存的实体;但是当 EF 提取新 ID 时,它发现两者具有相同的 ID。
长答案
删除
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
你的 OnModelCreating
方法应该是这样的。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Model Daughter:
var girlEntity = modelBuilder.Entity<Girl>();
girlEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
});
girlEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
girlEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
girlEntity.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
var boyEntity = modelBuilder.Entity<Boy>();
boyEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
});
boyEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
boyEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
boyEntity.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
如果您不想在配置中重复配置,我会使用基础 class 上的 DataAnnotations
属性来强制要求名称。
您还需要强制在数据库中自动生成 Id
属性。在 fluent API 中使用 Map
方法时,这不会按照惯例发生。您可以看到我在 Girl
和 Boy
映射中添加了流畅的调用以实现这一点。
希望这对您有所帮助。
I reworked Arturo's solution proposal. This solution was too long to
describe as a comment. So Arturo: thanks for giving me the ideas.
Chapeau!
Arturo 建议使用数据注释。我不想使用该方法的原因是,class 的建模不一定与特定的数据库表示相对应。我有点假设,但如果我想要男孩名字的最大长度小于女孩名字的最大长度,那么数据注释将无济于事。
此外,还有一些事情必须使用流利的方式才能完成API。例如,您不能使用 DataAnnotations 说 System.DateTime
在数据库中具有 DateTime2
格式。
如果您还没有猜到的话:我的问题描述非常简单。这三个 classes 都有很多属性需要大量流畅的 API 配置
Arturo 的评论帮助我找到了以下解决方案:
internal class ChildConfig<T> : EntityTypeConfiguration<T> where T : Child
{
public ChildConfig(...)
{
// configure all Child properties
this.Property(p => p.Name)....
}
}
internal class BoyConfig : ChildConfig<Boy>
{
public BoyConfig(...) : base (...)
{
// the base class will configure the Child properties
// configure the Boy properties here
this.Property(p => p.SomeBoyishProperty)...
}
}
在 MyDbContext 中:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BoyConfig(...));
modelBuilder.Configuration.Add(new GirlConfig(...));
}
总结:在Entity Framework中,我使用TPC创建了两个源自相同基础class的classes。在流利的 API 中,我映射了继承的属性,但是如何对基础属性进行建模 class?
更广泛的描述 在Entity Framework我有一个classChild,还有两种Children:男孩和女孩。 Boy 和 Girl 都派生自 Child:
public class Child
{
public int Id {get; set;}
public string Name {get; set;}
}
public class Boy : Child
{
public string SomeBoyishProperty {get; set;}
}
public class Girl : Child
{
public string SomeGirlyProperty {get; set;}
}
我想要一个 table 男孩和一个 table 女孩,每个 table 也有 Child 属性。
public class MyDbContext : DbContext
{
public DbSet<Boy> Boys {get; set;}
public DbSet<Girl> Girls {get; set;
}
From several sources, for example this one 我了解到这称为 TPC:table 每个具体 class 并且我应该在 OnModelCreating
中使用 MapInheritedPropertiesprotected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// model the properties of the base class, for instance set max length
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
// Model Daughter:
modelBuilder.Entity<Daughter>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
})
.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
modelBuilder.Entity<Son>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
})
.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
在 SaveChanges 期间,我收到一个 InvlidOperationException 指示主键不是唯一的。删除构建 Child 的部分解决了这个问题。
如何构建 Child 属性而不必在 Girl 和 Boy 属性中再次执行此操作?
简答:
如果您希望代码正常工作,请删除模型配置中对 Child
实体的所有引用。一旦 EF 知道 Child
作为实体,它将强制执行以下规则:不能有 2 个类型为 Child
的实体或 2 个从 Child
继承的实体在内存中具有相同的 PK。您可以看到错误告诉您成功保存的实体;但是当 EF 提取新 ID 时,它发现两者具有相同的 ID。
长答案
删除
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
你的 OnModelCreating
方法应该是这样的。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Model Daughter:
var girlEntity = modelBuilder.Entity<Girl>();
girlEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
});
girlEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
girlEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
girlEntity.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
var boyEntity = modelBuilder.Entity<Boy>();
boyEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
});
boyEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
boyEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
boyEntity.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
如果您不想在配置中重复配置,我会使用基础 class 上的 DataAnnotations
属性来强制要求名称。
您还需要强制在数据库中自动生成 Id
属性。在 fluent API 中使用 Map
方法时,这不会按照惯例发生。您可以看到我在 Girl
和 Boy
映射中添加了流畅的调用以实现这一点。
希望这对您有所帮助。
I reworked Arturo's solution proposal. This solution was too long to describe as a comment. So Arturo: thanks for giving me the ideas. Chapeau!
Arturo 建议使用数据注释。我不想使用该方法的原因是,class 的建模不一定与特定的数据库表示相对应。我有点假设,但如果我想要男孩名字的最大长度小于女孩名字的最大长度,那么数据注释将无济于事。
此外,还有一些事情必须使用流利的方式才能完成API。例如,您不能使用 DataAnnotations 说 System.DateTime
在数据库中具有 DateTime2
格式。
如果您还没有猜到的话:我的问题描述非常简单。这三个 classes 都有很多属性需要大量流畅的 API 配置
Arturo 的评论帮助我找到了以下解决方案:
internal class ChildConfig<T> : EntityTypeConfiguration<T> where T : Child
{
public ChildConfig(...)
{
// configure all Child properties
this.Property(p => p.Name)....
}
}
internal class BoyConfig : ChildConfig<Boy>
{
public BoyConfig(...) : base (...)
{
// the base class will configure the Child properties
// configure the Boy properties here
this.Property(p => p.SomeBoyishProperty)...
}
}
在 MyDbContext 中:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BoyConfig(...));
modelBuilder.Configuration.Add(new GirlConfig(...));
}