2017-08-30 63 views
2

简单地说,在C#EF6中,如何将两个导航属性映射到同一个表,同时保持它们的结果集是分开的?用简单的英语,我有一个班,我想在另一个班的两个集合。换句话说,我想要两个相同类型但具有不同元素的集合。不幸的是,EF6似乎将两个集合都视为相同,并为它们提供了相同的元素(表中的每条记录)。C#EF6:同一类型的两个导航属性

从数十个StackOverflow答案中找到的最好的是这个,但它有问题描述。在这个例子中,一个父亲有许多儿子和许多女儿,和他们每个人都有一个父亲。理想情况下,儿子女儿可以存储在同一张表儿童

class Father 
{ 
    [Key] 
    public long Id { get; set; } 

    public virtual ICollection<Child> Sons { get; set; } 

    public virtual ICollection<Child> Daughters { get; set; } 
} 

class Child 
{ 
    [Key] 
    public long Id { get; set; } 

    public long FatherId_1 { get; set; } 

    public Father Father_1 { get; set; } 

    public long FatherId_2 { get; set; } // One for each collection??? 

    public Father Father_2 { get; set; } 
} 

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Child>() 
      .HasRequired(e => e.Father_1) 
      .WithMany(e => e.Sons) 
      .HasForeignKey(e => e.FatherId_1); 
    modelBuilder.Entity<Child>() 
      .HasRequired(e => e.Father_2) 
      .WithMany(e => e.Daughters) 
      .HasForeignKey(e => e.FatherId_2); 
} 

这里的问题是,读取数据从儿童表回来的时候,它不会儿子和女儿区分。也就是说,儿子集合将不仅包含儿子女儿,并因此将女儿集合。我可能期望EF6尝试使用鉴别器列,但事实并非如此。

问题:如何将两个导航属性映射到同一个表并仍然能够将其记录读回到其相应的导航属性中?或者,这个例子是否正确,我的问题在其他地方?或者,这是不可能的,他们需要被映射到他们自己的表格(具有相同的模式)。

回答

1

我有点困惑,有你的解释。但我发现我了解一些代码:

对于“这是一个模式‘儿童’需要被分成两个集合”:

modelBuilder.Entity<Child>() 
.Map(m => 
    { 
    m.Properties(t => new { t.Id /*other props*/ }); 
    m.ToTable("Sons"); 
    }) 
.Map(m => 
    { 
    m.Properties(t => new { t.Id /*other props*/}); 
    m.ToTable("Daughters"); 
    }); 
+0

这是一个模型“孩子”,需要分成两个集合,而不是需要存储在一个表中的两个模型。你为什么要为模型中的同一逻辑类型创建两个类?你能告诉你如何在代码中解决上述问题吗? – drifter

+0

如果我正在阅读该权利,它将一些属性映射到一个表和其他属性到另一个表。是对的吗?我在第一段中澄清了我的问题。基本上,我有一个班,我想在另一个班有两个集合。这似乎是一种常见的模式。我很困惑为什么没有什么信息。 – drifter

0

我找到了一种方法通过使用来解决这个问题domain object backed by a state object。基本上,您使用状态对象以EF喜欢的方式存储数据,并且您的域对象将该数据公开给应用程序的其余部分。例如:

public class Father 
{ 
    //--- Constructor --- 

    internal Father(FatherState state) 
    { 
     State = state; 
    } 

    //--- Properties --- 

    public long Id => State.Id; 

    public IList<Child> Sons => Children.Where(child => child.Type == ChildType.Son).ToList().AsReadOnly(); 

    public IList<Child> Daughters => Children.Where(child => child.Type == ChildType.Daughter).ToList().AsReadOnly(); 

    //--- Methods --- 

    public void AddChild(Child child) 
    { 
     State.Children.Add(child); 
    } 

    public void RemoveChild(Child child) 
    { 
     State.Children.Remove(child); 
    } 
} 

internal class FatherState 
{ 
    [Key] 
    public long Id { get; set; } 

    public virtual ICollection<Child> Children { get; set; } 
} 

public class Child 
{ 
    [Key] 
    public long Id { get; set; } 

    public long FatherId { get; set; } 

    public Father Father { get; set; } 

    public ChildType Type { get; set; } 
} 

public enum ChildType 
{ 
    Son, 
    Daughter 
} 

当然,这只能与存储库模式中使用,因为它具有翻译由EF提供​​到由应用所消耗的Father对象FatherState对象。

我认为真正的解决办法是切换到fully featured ORM like NHibernate。 EF太不发达,而且这种情况在.NET Core中变得更糟。我甚至没有看到如何用EF做适当的DDD。许多开发人员必须在模型上妥协。 NHibernate有很多优点,我发现唯一的警告是它没有提供异步方法,但有onetwo的论点,它和它can be done anyway。还有NHibernate的forks,提供他们作为最后的手段。

0

我认为你完全不需要单独的映射来为儿子和女儿分割相同的FatherId。更好的解决方案是在Child Table(如enum)中创建一个唯一的标识符,如前面的答案所示,或者在Child模型中添加一个Gender字段,并且只保留一个Father模型的映射。 (我不知道你的具体要求,但是Father,Child也可以用严格的面向对象术语在一个超类humanperson中概括)。

但由于我不确定你的要求,如果你真的想继续你的映射,那么我提出的解决方案是这样的。

class Father 
    { 
     [Key] 
     public long Id { get; set; } 

     public int SonId{get;set;} 
     public int DaughterId{get;set;} 


     [ForeignKey("SonId")] 
     public virtual Child Child_Son{get;set;} 

     [ForeignKey("DaughterId")] 
     public virtual Child Child_Son{get;set;} 
    } 

    class Child 
    { 
     [Key] 
     public long Id { get; set; } 

     public string Gender{get;set;} 
    } 

说明

两名外籍每个键SonFather类相同Child类的Daughter将轻松帮您在使用基本的LINQ或任何SQL查询实现单独收集。此外,这保留了“如果父母只有孩子存在”的规则。

+0

谢谢,我会试试这个并报告回来。 – drifter

+0

我很难实现它,但我无法确认或否认它。如果我尝试更长时间,我可能已经开始工作。我时间很短,决定将所有记录映射到一个ICollection属性,然后使用访问器属性对结果进行分类和排序(与我的答案类似)。它保持我的数据库设计清晰,代码易于理解。 – drifter

相关问题