简单地说,在C#EF6中,如何将两个导航属性映射到同一个表,同时保持结果集分开?用简单的英语,我有一个班级,我想要在另一个班级的两个集合。换句话说,我想要两个相同类型但具有不同元素的集合。不幸的是,EF6似乎对两个集合都是一样的,并且给它们两个相同的元素(表中的每个记录)。
我从几十个StackOverflow答案中找到的最好的是这个,但它有问题描述。在这个例子中,父有许多儿子和许多女儿,他们每人都有一个父。理想情况下, Sons 和 Daughters 都可以存储在同一个表 Child 中。
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);
}
问题在于,当从儿童表中读取数据时,它不区分儿子和女儿。也就是说, Sons 集合不仅包含 Sons ,还包含 Daughters , Daughters 集合也是如此。我可能期望EF6尝试使用鉴别器列,但事实并非如此。
问题:如何将两个导航属性映射到同一个表,并且仍然能够将其记录读回相应的导航属性?或者,示例是否正确,我的问题在其他地方?或者,这是不可能的,并且它们需要映射到它们自己的表(具有相同的模式)。
答案 0 :(得分:1)
我有点困惑,你的解释。但我找到了一些我理解的代码:
对于&#34;它是一个模型&#34; Child&#34;需要分成两个集合&#34; :
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");
});
答案 1 :(得分: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提供的FatherState
对象转换为应用程序使用的Father
对象。
我认为真正的解决方案是切换到fully featured ORM like NHibernate。 EF太不发达了,而且只有.NET Core才会变得更糟。我甚至不知道如何用EF做适当的DDD。许多开发人员必须妥协他们的模型。 NHibernate有很多优点,我发现唯一的警告是它没有提供异步方法,但有one或two个参数反对它,它can be done anyway。还有forks的NHibernate将它们作为最后的手段提供。
答案 2 :(得分:0)
我认为你不需要单独的映射来为儿子和女儿分岔同样的父亲。更好的解决方案是在Child Table
中创建唯一标识符,例如enum
,如前面的答案所示,或在Gender
模型中添加Child
字段,并且只保留{Father
的一个映射1}}模型。 (我不知道您的确切要求,但Father
,Child
也可以在严格的面向对象术语中的一个超类human
或person
中进行推广。
但由于我不确定你的要求,如果你真的想继续你的映射,那么我建议的解决方案就是这个。
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;}
}
<强>解释强>:
Son
类中同一Daughter
类Child
和Father
的两个外键可以轻松帮助您使用基本Linq或任何SQL查询实现单独的集合。此外,这保留了规则“如果父母只存在,那么孩子就存在”。
答案 3 :(得分:0)
我在你的建模中可以看到,子实体需要具有属性fatherId和gender。检查以下代码:
public class Child
{
[Key]
public long Id {get; set;}
public string Name {get; set;}
public Gender Gender{get; set;} //Usually use Enum containing Female and Male
public long FatherId{get; set;}
public virtual Father Father {get; set;}
}
public class Father
{
public Father()
{
Children = new HashSet<Child>();
}
[Key]
public long Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Child> Children{get; set;}
}
以性别为基础的孩子将适用于您想要做的事情。