C# 将引用属性映射到抽象父级
我在企业应用程序中有一个复杂的对象层次结构。我会尽量让它简单、抽象,但仍然能代表我正在处理的问题 我的项目处理同一类型对象的几种样式。为此,我们为实体对象实现了TPT结构:C# 将引用属性映射到抽象父级,c#,entity-framework,inheritance,entity-framework-6,tph,C#,Entity Framework,Inheritance,Entity Framework 6,Tph,我在企业应用程序中有一个复杂的对象层次结构。我会尽量让它简单、抽象,但仍然能代表我正在处理的问题 我的项目处理同一类型对象的几种样式。为此,我们为实体对象实现了TPT结构: public abstract class BaseWidget { public int Id { get; set; } // etc... } // About a dozen concrete implementations already exist and work great! public
public abstract class BaseWidget {
public int Id { get; set; }
// etc...
}
// About a dozen concrete implementations already exist and work great!
public class ExistingWidget : BaseWidget {
// Other properties
}
现在我有一个新的类型,我正在做。我们在对象上有共同的属性,但根据子类型,需要一些不同的细节集。为此,我设置了TPH,因为该类型的属性在所有子类型中都是相同的。唯一的区别是需要哪些详细信息对象
public abstract NewWidgetBase : BaseWidget {
public int EmployeeNumber { get; set; }
public DateTime EffectiveDate { get; set; }
}
public NewWidgetA : NewWidgetBase {
}
public NewWidgetB : NewWidgetBase {
}
我将其映射到我的DbContext中,如下所示:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<NewWidgetBase>()
.Map<NewWidgetA>(w => w.Requires("Discriminator").HasValue("a"))
.Map<NewWidgetB>(w => w.Requires("Discriminator).HasValue("b"));
CreateTable(
"dbo.BaseWidget",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ExistingWidget",
c => new
{
Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.NewWidgetBase",
c => new
{
Id = c.Int(nullable: false),
EmployeeNumber = c.Int(nullable: false),
EffectiveDate = c.DateTime(nullable: false),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.FooDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.BarDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
然后,我将这些引用属性添加到相应的NewWidget
对象中
public class NewWidgetA {
// ...
public FooDetails Foo { get; set; }
}
public class NewWidgetB {
// ...
public FooDetails Foo { get; set; }
public BarDetails Bar { get; set; }
}
我尝试执行此操作,假设典型映射可以工作,并得到以下错误:
System.Data.Entity.Infrastructure.DbUpdateException:保存未公开其关系的外键属性的实体时出错。EntityEntries属性将返回null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅InnerException。-->System.Data.Entity.Core.UpdateException:无法确定依赖操作的有效顺序。依赖关系可能由于外键约束、模型要求或存储生成的值而存在
有了这些,我明白了它没有正确的关系方向和键映射。所以我再次在DbContext中显式地设置它:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredDependent();
但是,这也会产生一个错误:
类型“…Foo”的属性“Widget”上的ForeignKeyAttribute无效。在依赖类型“NewWidgetA”上未找到外键名称“WidgetId”。名称值应为以逗号分隔的外键属性名称列表
这让我相信,我无法用抽象属性做我想做的事情。有没有一种方法可以描绘出我所错过的这种关系?我不想每个都有一个特定的引用属性,因为我知道在一两个月内会有更多的类型,并且属性列表会变得很难处理。这是可能的,但只能使用单向(导航属性只在
小部件
侧)一对一,其中,小部件
侧是主体,详细信息
侧是从属
首先从详细信息
实体中删除导航和FK属性:
public class FooDetails {
public int Id { get; set; }
// ...
}
public class BarDetails {
public int Id { get; set; }
// ...
}
并使用以下fluent配置:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredPrincipal();
令人惊叹的!这是有道理的,但有点违反直觉。我一开始工作就试试看。
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredPrincipal();
CreateTable(
"dbo.BaseWidget",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ExistingWidget",
c => new
{
Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.NewWidgetBase",
c => new
{
Id = c.Int(nullable: false),
EmployeeNumber = c.Int(nullable: false),
EffectiveDate = c.DateTime(nullable: false),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.FooDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.BarDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);