【问题标题】:Entity Framework with Include / Joining Issues包含/加入问题的实体框架
【发布时间】:2011-12-15 03:40:55
【问题描述】:

我已经实现了一个表继承功能,在 http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server 上演示。

所有外键和约束都已到位。

现在我正在使用实体框架来拉回我的人员、学生、教师和家长,其中模型看起来如下所示(没有所有 EF 特定属性等)。

public partial class People : EntityObject
{
  public guid PeopleID { get; set; }
  public int Age { get; set; }  /Added for an example query
  public PeopleParent Parent { get; set; }
  public PeopleStudent Student { get; set; }
  public PeopleTeacher Teacher { get; set; }
}

现在我需要获取所有类型的人,年龄为 25 岁,不超过 100 条记录,并且我想包含所有引用的数据。我创建我的 EF 查询,如:

IQueryable<People> query = Entities.People.Include("PeopleParent")
                                           .Include("PeopleStudent")
                                           .Include("PeopleTeacher");

query.Where(x => x.Age == 25)
     .Take(100);

IEnumerable<People> results = query.ToList();

看起来很简单,但无论我设置包含什么表/实体集,首先都会创建一个INNER JOIN 而不是LEFT OUTER JOIN,这不会产生正确的结果。

生成的 TSQL(不符合我的需要):

SELECT 
  [Limit1].[C1] AS [C1],
  <A bunch of Limit1 Columns>
FROM (
  SELECT TOP (100) 
    [Extent1].[PeopleID] AS [PeopleID],
    <A bunch of Extent1 Columns>
    [Extent2].[PeopleID] AS [PeopleID1], 
    <A bunch of Extent2 Columns>
    [Extent3].[PeopleID] AS [PeopleID2], 
    <A bunch of Extent3 Columns>
    [Extent4].[PeopleID] AS [PeopleID3], 
    <A bunch of Extent4 Columns>
    1 AS [C1]
  FROM [rets].[People] AS [Extent1]
  INNER JOIN [rets].[PeopleParent] AS [Extent2] 
    ON [Extent1].[PeopleID] = [Extent2].[PeopleID]
  LEFT OUTER JOIN [rets].[PeopleStudent] AS [Extent3] 
    ON [Extent1].[PeopleID] = [Extent3].[PeopleID]
  LEFT OUTER JOIN [rets].[PeopleTeacher] AS [Extent4] 
    ON [Extent1].[PeopleID] = [Extent4].[PeopleID]
) AS [Limit1]

为什么第一个 Include 被用作 INNER JOIN,我的问题有解决方案吗?

** 更新 1**

假设我使用 Ladislav Mrnka 的 Answer,由于 Linq 和 Lambda 查询的重大变化,还有两个额外的要求。

问题:如何搜索具有特定属性的特定人物?

(所有成绩为“A”的学生)

答案:

context.People.OfType<Student>().Where(s => s.Grade == "A");

问题:如何搜索具有特定属性的任何人?

(PrimaryFocus 的所有学生或教师 = “数学”)

答案:

List<People> result = new List<People>();
result.AddRange(context.People.OfType<Student>()
                              .Where(x => x.PrimaryFocus == "Math")
                              .ToList());
result.AddRange(context.People.OfType<Teacher>()
                              .Where(x => x.PrimaryFocus == "Math")
                              .ToList());

【问题讨论】:

    标签: c# linq tsql entity-framework-4


    【解决方案1】:

    对您来说显而易见的解决方案应该是使用原生 EF 支持来进行继承。在你的情况下TPT inheritance。一旦你有了继承,你只需调用:

    IEnumerable<People> results = Entities.People
                                          .Where(x => x.Age == 25)
                                          .Take(100)
                                          .ToList();
    

    它会返回StudentTeachersParents等实例。

    在您的解决方案中,唯一的建议是检查关系是否是可选的 (1 - 0..1) - 。如果需要,它将使用INNER JOIN。如果它是可选的并且仍然使用INNER JOIN,则可能是您的模型中的一些错误或其他问题。

    【讨论】:

    • 我没有在 EF 中使用继承支持。如果我选择走下这个“兔子洞”来使用继承,当父母和老师没有Grade 属性(仅限学生)时,如果我查找.Where(x =&gt; x.Grade == "A"),它会如何影响搜索?
    • 您不能搜索People,而是搜索Studentscontext.People.OfType&lt;Student&gt;().Where(s =&gt; s.Grade == "A")。继承是棘手的,它具有显着的性能成本(这将在 .NET 4.5 中得到改进),但它是您数据库的最佳解决方案。
    • 第二个问题可能没有简单的答案。除非共享属性在 Person 类中,否则您必须分别查询学生和教师。您可以尝试使用两个查询的Union 的一些技巧,但是由于StudentTeacher 查询的联合将不起作用(它们属于不同类型),因此转换会出现问题。 EF 是关于考虑面向对象的查询,因此并非每个关系查询都可以直接翻译。
    • 由于我返回IENumerable&lt;People&gt;,我应该能够进行两个单独的查询并将两个查询结果合并为一个结果。
    • 它适用于隐式转换为 People 的 Linq-to-objects,但可能不适用于联合集必须具有相同列的 Linq-to-entities。因此,您必须分别执行这些查询,并在从数据库返回后将它们合并到单个 IEnumerable
    猜你喜欢
    • 1970-01-01
    • 2017-09-02
    • 2010-09-29
    • 2015-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    相关资源
    最近更新 更多