【问题标题】:How do I query and display child and grandchild data in EF 6 and MVC 5?如何在 EF 6 和 MVC 5 中查询和显示子孙数据?
【发布时间】:2017-04-13 03:55:52
【问题描述】:

我有一个使用 EF 6 和 MVC 5 的应用程序,可以很好地输入数据,但现在当我尝试显示其中的一些数据时,我遇到了麻烦。我的实体的基本布局如下图所示:

我遇到问题的第一部分是查询和过滤数据。我想返回存在调查和签核但没有批准的场所和相关数据的列表。在直接 SQL 中,现在可以使用的查询是:

SELECT * 
FROM Premises p LEFT OUTER JOIN Approvals a ON a.Id = p.Id
JOIN Surveys s ON s.PremiseId = p.Id
JOIN SignOffs so ON so.Id = s.Id
WHERE a.ApprovedBy IS NULL

我开始的代码是这样的:

var premises = Premises.Include(p => p.Approval)
    .Include(p => p.Surveys)
    .Include(p => p.Surveys.Select(s => s.SignOff));

似乎*返回包括子数据在内的所有记录,但是当我尝试过滤它以便只获得具有签核记录但没有批准的记录时,它没有工作。

var premises = Premises.Include(p => p.Approval).Where(p => p.Approval.ApprovedBy == null)
    .Include(p => p.Surveys)
    .Include(p => p.Surveys.Select(s => s.SignOff).Where(s => s.Signature != null));

如果我使用此代码,我会收到此错误:

包含路径表达式必须引用在类型上定义的导航属性。对引用导航属性使用虚线路径,对集合导航属性使用 Select 运算符。 参数名称:路径

我已经改变了这个查询很多来尝试不同的事情,所以我不确定我做了什么,但我认为第一个 Where 语句可能会自行工作,但第二个肯定会导致错误。 我需要如何构造我的查询以使其返回正确过滤的请求数据?

另外,我在上面加了一个星号,表示查询似乎返回所有数据和子数据,因为我实际上无法对其进行测试。当我试图为此编写我的 Razor CSHTML 页面时,它并没有给我对子数据和孙数据的智能感知,如果我输入我认为应该是的内容,我会收到错误。我如何需要在页面上引用这些数据?

【问题讨论】:

    标签: entity-framework asp.net-mvc-5


    【解决方案1】:

    您不能像这样使用Include(),它只适用于指定加载导航属性,而不是指定在导航属性为某物时加载实体(在您的情况下不是null)。

    要进行过滤,我建议如下:

    var premises = Premises.Include(p => p.Approval).Include(p => p.Surveys).Include(p => p.Surveys.Select(s => s.SignOff))
                   .Where(p=>p.Approval.ApprovedBy!=null && p.Surveys.Any(s=>s.SignOff.Signature!=null));
    

    所以基本上,包含和过滤彼此无关。使用包含,您只需指定要加载的内容,您仍然可以在原始实体集上使用过滤器。

    【讨论】:

      【解决方案2】:

      您对Include LINQ 方法的作用感到困惑。它只告诉 EF 急切地加载该关系,如果您的查询本身利用该关系,这实际上是不必要的;在这种情况下,EF 将默认包含该关系。

      它不允许您过滤这些关系。例如,在这部分代码中:

      .Include(p => p.Surveys.Select(s => s.SignOff).Where(s => s.Signature != null));
      

      where 子句适用于Premises不是 SignOff,正如您所想的那样。换句话说,Where 过滤正在查询的主表,而不是您包含的表。

      这里有两条前进的道路。您可以简单地按重要部分过滤Premises,即:

      var premises = Premises.Where(p => p.Approval.ApprovedBy == null && p => p.Surveys.Any(s => s.SignOff.Signature != null));
      

      这将仅返回满足这些条件的前提,但包含的 Surveys 集合将包含与每个前提相关的所有调查,而不仅仅是具有空签核签名的调查。

      如果您还需要过滤相关项目,则必须显式加载它们:

      foreach (premise in premises)
      {
          context.Entry(premise) 
              .Collection(p => p.Surveys) 
              .Query() 
              .Where(s => s.SignOff.Signature != null) 
              .Load();
      }
      

      注意两点:

      1. 由于必须应用此查询的性质,无法对所有场所执行一次。您必须遍历该场所并为每个场所显式加载 Surveys 集合。

      2. 由于这将发出一个新查询,因此您希望避免在此显式加载之前延迟或急切地加载 Surveys 集合。否则,您将两次查询相同的信息,这是非常低效的。确保这一点的最简单方法是从集合属性中删除 virtual 关键字。但是,如果您这样做,那么您将不得不急切或显式加载集合,否则它将为空。欲了解更多信息,请参阅:https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

      【讨论】:

        猜你喜欢
        • 2013-01-25
        • 2015-05-31
        • 2018-11-09
        • 2014-11-05
        • 2016-11-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-24
        • 1970-01-01
        相关资源
        最近更新 更多