Linq To Entities 查询

4
请考虑以下查询语句:
var profilelst =
    (
        from i in dbContext.ProspectProfiles
        where i.CreateId == currentUser
        select new ProspectProfile
        {
            ProspectId = i.ProspectId,
            Live = i.Live,
            Name = i.Name,
            ServiceETA = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
                .FirstOrDefault()
                .ServiceETA.ToString(),
            FollowUpDate = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
                .FirstOrDefault()
                .FollowUpDate
        }
    )
    .ToList();

return profilelst.OrderByDescending(c=>c.FollowUpDate)
        .Skip(0).Take(endIndex)
        .ToList();

在这个查询中,请查看FollowUpDateServiceType,这两个都是我从Opportunity表中获取的,是否有其他方法可以获得这两个字段。
表中的一对多关系类似于:ProspectProfile -> Opportunities 是否我编写的查询可以使用或者还有其他更简单的方法。

重新格式化代码以使其易读,但实际问题并不清楚。 - Danny Varod
你测试过你的第一个查询是否有效吗?我有疑问。 - Slauma
2个回答

4
您可以通过将代码更改为以下内容来避免重复订购,这是您唯一可以改进的地方:
var profilelst
  = dbContext.ProspectProfiles
             .Where(i => i.CreateId == currentUser)
             .Select(i => 
                    {
                        var opportunity
                           = i.Opportunities
                              .OrderByDescending(t => t.FollowUpDate)
                              .First();
                        return new ProspectProfile
                        {
                            ProspectId = i.ProspectId,
                            Live = i.Live, 
                            Name = i.Name,
                            ServiceETA = opportunity.ServiceETA.ToString(),
                            FollowUpDate = opportunity.FollowUpDate
                        }
                    }).ToList();

return profilelst.OrderByDescending(c => c.FollowUpDate).Take(endIndex).ToList();

我对您原本的查询做了几个改变:

  1. 我将其改为使用方法链语法。在我看来,这样阅读起来更容易。
  2. 我去掉了不必要的 Skip(0)
  3. 最大的变化在于 Select 部分:
    1. 我将 FirstOrDefault 改为 First,因为您无论如何都会访问返回值的属性。这将抛出一个描述性异常(如果没有机会存在),比您原来的情况要好:在您的情况下,它会抛出一个 NullReferenceException。那很糟糕,NullReferenceExceptions 总是表示程序中存在 bug,一点都不描述性。
    2. 我将选择机会的部分移出了初始化器,这样我们只需要对其进行一次排序,而不是两次。

我认为这不适用于LINQ to Entities,因为不支持将投影到实体中,First()在投影中也不被支持,而且我怀疑在Select表达式中使用本地变量是否被支持。 - Slauma
@Slauma:好观点。能否提供另一种解决方案? - Daniel Hilgarth
@DanielHilgarth:我现在尝试着回答。我没有立即回答,因为我对问题中的查询有些困惑,这个查询部分具有与您的查询相同的缺陷。同时,我相信问题中的查询未经测试,也无法正常工作。 - Slauma

1

您的查询存在一些问题:

  • 您不能将投影到实体中 (select new ProspectProfile)。LINQ to Entities 仅支持投影到匿名类型 (select new) 或其他不属于实体数据模型的类型 (select new MySpecialType)。

  • 在 LINQ to Entities 中不支持对数字或 DateTime 类型使用 ToString() (ServiceETA.ToString())。

  • 如果 Opportunities 集合为空且 ServiceETA 是非空值类型 (例如 DateTime),则 FirstOrDefault().ServiceETA (或 FollowUpdate) 将引发异常,因为 EF 无法将任何值实例化到这样的变量中。

  • 在第一个查询之后使用 .ToList() 将在数据库中执行查询并加载完整结果。您稍后的 Take 在完整列表中进行内存操作,而不是在数据库中进行操作。 (您实际上将整个结果列表从数据库加载到内存中,然后扔掉除第一个以外的所有对象,您已经 Takeen.)

为解决这四个问题,您可以尝试以下方法:
var profilelst = dbContext.ProspectProfiles
    .Where(p => p.CreateId == currentUser)
    .Select(p => new
    {
        ProspectId = p.ProspectId,
        Live = p.Live,
        Name = p.Name,
        LastOpportunity = p.Opportunities
           .OrderByDescending(o => o.FollowUpDate)
           .Select(o => new
           {
               ServiceETA = o.ServiceETA,
               FollowUpDate = o.FollowUpDate
           })
           .FirstOrDefault()
    })
    .OrderByDescending(x => x.LastOpportunity.FollowUpDate)
    .Skip(startIndex)  // can be removed if startIndex is 0
    .Take(endIndex)
    .ToList();

这将给你一个匿名对象列表。如果你需要结果以你的实体ProspectProfile列表的形式呈现,你必须在此查询后复制这些值。请注意,如果一个ProspectProfile没有Opportunities,那么结果中的LastOpportunity可能为null


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接