Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将引用属性映射到抽象父级_C#_Entity Framework_Inheritance_Entity Framework 6_Tph - Fatal编程技术网

C# 将引用属性映射到抽象父级

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

我在企业应用程序中有一个复杂的对象层次结构。我会尽量让它简单、抽象,但仍然能代表我正在处理的问题

我的项目处理同一类型对象的几种样式。为此,我们为实体对象实现了TPT结构:

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);