【问题标题】:Linq To Entities QueryLinq 到实体查询
【发布时间】:2012-01-25 12:33:26
【问题描述】:

考虑以下查询:

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

我写的查询是否正常,或者是否有其他可以更简单的方法解决。

【问题讨论】:

  • 重新格式化代码使其可读,但实际问题并不清楚。
  • 您是否测试过您的第一个查询是否有效?我有疑问。

标签: c# asp.net entity-framework linq-to-entities


【解决方案1】:

您唯一可以改进的是通过将代码更改为这样来避免订购两次:

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 总是表示您的程序中存在错误,而且根本不是描述性的。
    2. 我将选择机会的部分移出初始化程序,因此我们只需要进行一次排序而不是两次。

【讨论】:

  • 我认为这不适用于 LINQ to Entities,因为不支持投影到实体中,投影中不支持 First(),我怀疑在支持Select 表达式。
  • @Slauma:好点。愿意提供替代解决方案吗?
  • @DanielHilgarth:我现在已经尝试了答案。我没有立即回答,因为我对问题中的查询感到困惑,该查询与您的查询有部分相同的缺陷。同时,我认为问题中的查询未经测试,也不起作用。
【解决方案2】:

你的查询有不少问题:

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

  • LINQ to Entities (ServiceETA.ToString()) 不支持数字类型的ToString()DateTime 类型(ServiceETA.ToString())

  • FirstOrDefault().ServiceETA(或FollowUpdate)将在Opportunities 集合为空且ServiceETA 是不可为空的值类型(例如DateTime)时抛出异常,因为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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多