首页 > 解决方案 > 具有 1 对 1 关系实体框架的抽象类

问题描述

使用实体框架我一直在尝试创建这种关系。基本上我有 1 个具有Result. 该Result对象是抽象的,因为它必须是继承自 的 3 个类之一Result,即ApprovedRejectedModified

在此处输入图像描述

我正在尝试使用实体框架创建表结构。最初我打算使用 TPCT(每个具体类型的表)结构,所以不会有表,但如果我想引用,我想Result将链接保留在表中,所以现在我只尝试 TPT 结构。我发现 TPCT 更干净,但最终如果 TPT 是实现我想要的唯一方法,我可以接受。ActionResult

我为我的模型结构尝试了以下变体:

public class Action 
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}

    public int Result_Id {get; set;}

    [ForeignKey("Result_Id")]
    public virtual Result Result {get; set;}

    public string Description {get; set;}
}

public abstract class Result
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}

    [Required]
    public int Action_Id {get; set;}

    [ForeignKey("Action_Id")]
    public virtual Action Action {get; set;}

    public string Comment {get; set;}


    public class Approved : Result
    {
        public string Thing {get; set;}
    }

    public class Rejected : Result
    {
        public string Stuff {get; set;}
    }

    public class Modified : Result
    {
        public string Whatever {get; set;}
    }
}

然后我在我的上下文文件中尝试了以下两种策略来实现 TPT:

modelBuilder.Entity<Approved>().ToTable("Approved");
modelBuilder.Entity<Rejected>().ToTable("Rejected");
modelBuilder.Entity<Modified>().ToTable("Modified");

或者对于 TCPT:

modelBuilder.Entity<Approved>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Approved");
});

modelBuilder.Entity<Rejected>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Rejected");
});

modelBuilder.Entity<Modified>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Modified");
});

每次我尝试添加新的迁移时,无论我尝试什么,我都会遇到这个错误: Unable to determine the principal end of an association between the types 'Result' and 'Action'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

我能够让它工作的一次是,如果我从Action课堂上删除了这个引用:

public int Result_Id {get; set;}

[ForeignKey("Result_Id")]
public virtual Result Result {get; set;}

但我真的很想把那个引用保留在那里,所以当我进入我的数据库来获取那个Action对象时,我可以立即判断它是否有Result关联,而不必遍历所有 3 个结果表来查看是否有参考Action(这就是为什么我认为我需要 TPT ......)

任何帮助来完成这项工作将不胜感激!

标签: c#entity-frameworkinheritanceforeign-keysabstract-class

解决方案


经过大量的研究和反复试验,我发现了获得想要的结果所需的东西。它是 TPCT DB 结构,并且Action对象能够保留对Result. 以下是模型类:

public class Action 
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}

    public virtual Result Result {get; set;} //just virtual here, as Action is the dependent and Result is the principal-- i.e. this Result isn't required

    public string Description {get; set;}
}

public abstract class Result
{
    //got rid of the Result_Id, since it's 1:1 the Action_Id can be the Key

    [Required, Key] //added "Key"
    public int Action_Id {get; set;}

    [ForeignKey("Action_Id")]
    public Action Action {get; set;} //removed this virtual, as Action is Required for Result, that makes Result the principal

    public string Comment {get; set;}


    public class Approved : Result
    {
        public string Thing {get; set;}
    }

    public class Rejected : Result
    {
        public string Stuff {get; set;}
    }

    public class Modified : Result
    {
        public string Whatever {get; set;}
    }
}

这是上下文中的流畅 API 代码:

//this gave me TPCT like I wanted
modelBuilder.Entity<Approved>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Approved");
});

modelBuilder.Entity<Rejected>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Rejected");
});

modelBuilder.Entity<Modified>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("Modified");
});

//this defined the principal-dependent relationship I was missing
modelBuilder.Entity<Action>()
    .HasOptional(a => a.Result)
    .WithRequired(a => a.Action)
    .Map(x => x.MapKey("Action_Id"));

然后它起作用了!希望这个例子可以帮助别人。


推荐阅读