为什么Linq on date range在组合查询上返回null,在单独的查询上工作正常

问题描述 投票:1回答:3

问题:在日期范围内过滤记录并使用LINQ匹配CityID时,查询在以两个步骤编写时成功;但是,当它组合成一个查询时它会失败!

如何重写LINQ查询,以便 - 它可以执行两个过滤器(即匹配CityId并在同一步骤中检索date range中的记录以提高性能?

我让它分两步工作,

即做一个

var Step1 = db.weekRecord.Where(x => x.CityId == CityRecord.Id).ToList();

然后

Step1.Where(x => x.date.Date >= fromDate.Date 
                 && x.date.Date <= toDate.Date)
     .ToList();

我把它们结合起来就失败了!!

  // works when done in 2 steps!! 
  var weeklyWeather = db.weekRecord
    .Where(x => x.CityId == CityRecord.Id 
      && (x.date >= weekStarting && x.date <= weekEnding))

  // - when combined results are NULL!??
  var weeklyWeather2 = 
    db.weekRecord(x => x.date.Date >= fromDate.Date && x.date.Date <= toDate.Date)
      .ToList();

在查找其他SO answers后,我尝试了这个TruncateTime ......无法让它工作..

// is this correct, from SO answers, DbFunctions.TruncateTime
var testQueryRecrods = db.weekRecord
  .Where(x => x.CityId == CityRecord.Id)
  .Where(x => 
    DbFunctions.TruncateTime(x.date.Date) >= DbFunctions.TruncateTime(fromDate.Date) 
    && DbFunctions.TruncateTime(x.date.Date) <= DbFunctions.TruncateTime(toDate.Date))
  .ToList();

错误:

[NotSupportedException:LINQ to Entities中不支持指定的类型成员'Date'。仅支持初始化程序,实体成员和实体导航属性。 System.Data.Entity.Core.Objects.ELinq.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent,MemberExpression linq)+452 System.Data.Entity.Core.Objects.ELinq.TypedTranslator`1.Translate(ExpressionConverter parent,Expression linq)+49

c# sql entity-framework linq datetime
3个回答
1
投票

问题很困惑,但我认为问题是.Date。与linq2sql不同,实体框架无法将.Date转换为sql。但你可以改写它

var fromDateDate = fromDate.Date;
var toDateDate = toDate.Date;

var testQueryRecrods = db.weekRecord
                .Where(x => x.CityId == CityRecord.Id)
                .Where(x => DbFunctions.TruncateTime(x.date) >= fromDateDate
                         && DbFunctions.TruncateTime(x.date) <= toDateDate)
                .ToList();

它会起作用。到某一点。在这种情况下,EF产生的实际上是完全愚蠢的。与linq2sql不同,EF生成查询,这不是sargable(在我的情况下*)。它可以比必要的慢几千倍。我建议完全避免转换到日期:

var fromDateDate = fromDate.Date;
var toDateDate1 = toDate.Date.AddDays(1);

var testQueryRecrods = db.weekRecord
                .Where(x => x.CityId == CityRecord.Id)
                .Where(x => x.date >= fromDateDate 
                         && x.date < toDateDate1)
                .ToList(); 

正如@juharr指出的那样,当你拆分查询时,你会在服务器上运行前半部分,而在后半部分运行linq到对象。在这种情况下,.Date可以工作,但是你在上半年下载了比你需要的更多的记录。

*日期时间类型可能是问题,也许它会更好地使用datetime2,我没有测试这个场景


0
投票

最好的建议是为此编写自己的LINQ扩展。

public static class ext
{
    //This extension compares one date to another... if you can call from Linq
    public static bool GreaterThan(this DateTime self, DateTime CompareDate
    {
    if (self.Year > CompareDate.Year) return true;

    else if ((self.Year == CompareDate.Year) && (self.Month > CompareDate.Month)
      return true;

    else if ((self.Year == CompareDate.Year) && (self.Month ==  CompareDate.Month)  && (self.Day > CompareDate.Day))
      return true;

    return false;
    }

}

0
投票

我认为,除了使用DbFunctions.TruncateTime for Linq to Entities之外别无选择。因为,作为SQL Server查询Linq to Entities应该执行转换datetimedate并且可以使用的最佳方法是DbFunctions.TruncateTime。我只是调试了DbFunctions.TruncateTime转换和翻译的查询似乎;

WHERE (convert (datetime2, convert(varchar(255), [Extent1].[CreationDate], 102) ,  102)) > @p__linq__0

如您所见,在执行对话时,此处存在冗余的字符串对话。但是,EF会将SQL中的日期时间转换为日期,就像这个'cast(CreationDate as date)'一样。但事实并非如此。

所以,这里有两种选择。

1-如果你有一个非常庞大的表,性能受冗余字符串对话的影响,你应该在SQL中手动构建你的查询作为存储过程或类似的东西,并从上下文执行它。

2-如果你没有这样的性能考虑;只需使用DbFunctions.TruncateTime(x.date)

var testQueryRecrods = db.weekRecord
    .Where(x => x.CityId == CityRecord.Id)
    .Where(x => DbFunctions.TruncateTime(x.date) >= fromDate.Date && DbFunctions.TruncateTime(x.date) <= toDate.Date)
    .ToList();
© www.soinside.com 2019 - 2024. All rights reserved.