Linq to Entities 日期部分优化

Posted

技术标签:

【中文标题】Linq to Entities 日期部分优化【英文标题】:Linq to Entities Date Part Optimization 【发布时间】:2015-05-31 22:54:09 【问题描述】:

我正在尝试根据网站管理仪表板的日期字段的一部分(来自区间分析图表)创建向下钻取。

var drilldownQuery = DataManager.DataSessions
            .Include("Location")
            .Include("Quote.Carriers")
            .Include("Drivers")
            .Include("Vehicles")
            .Where(session =>
                session.Timestamp >= Model.FromDate &&
                session.Timestamp < through
            );

        if (!String.IsNullOrWhiteSpace(Model.DrillDown))
        
            drilldownQuery = drilldownQuery.ToList()
                .Where(session => 
                   IntervalSelector(session) == Model.DrillDown);
        

    public string IntervalSelector(DataSession session)
    
        switch (Model.SelectedInterval)
        
            case TimeInterval.Hourly:
                return session.Timestamp.Hour.ToString("D2");
            case TimeInterval.Weekday:
                return ((int)session.Timestamp.DayOfWeek).ToString();
            case TimeInterval.Weekly:
                return session.Timestamp.Date.AddDays(-(int)session.Timestamp.DayOfWeek).ToString("yyyy/MM/dd");
            case TimeInterval.Monthly:
                return session.Timestamp.Date.ToString("yyyy/MM");
            case TimeInterval.Annual:
                return session.Timestamp.Year.ToString("D4");
            default:
                return session.Timestamp.Date.ToString("yyyy/MM/dd");
        
    

当然,这在较大的日期范围内表现很差。我希望避免“ToList()”调用,以便在数据库而不是内存中执行向下钻取标准。这就是我卡住的地方,特别是在深入研究每小时和工作日标准时。

IntervalSelector 还用于对图形查询进行分组以及向下钻取。我愿意使用单独的选择器进行向下钻取,无论怎样提高它的性能。图形查询也执行 ToList,但性能很好,因为它不需要包含。

var graphQuery = DataManager.DataSessions
            .Where(session =>
                session.Timestamp >= Model.FromDate &&
                session.Timestamp < through);

我想更好的方法是将 switch 语句移到 Linq 之外,如下所示:

switch (Model.SelectedInterval)

    case TimeInterval.Hourly:
        int selectedHour = int.Parse(Model.DrillDown);
        drilldownQuery = drilldownQuery
            .Where(session => session.Timestamp.Hour == selectedHour);
        // thoughts on this? any better way?
        break;
    case TimeInterval.Weekday:
        var selectedWeekday = int.Parse(Model.DrillDown) + 1; 
             // convert to sql dayofweek (Sun = 1)
        drilldownQuery =
            drilldownQuery.Where(
                 session => SqlFunctions
                      .DatePart("weekday", session.Timestamp) == 
                            selectedWeekday);
         // get a NotSupportedException here.
         // This function can only be invoked from Linq to Entities
         break;
    case TimeInterval.Weekly:
    case TimeInterval.Monthly:
    case TimeInterval.Annual:
    default:
        // handle these by adjusting the from & through dates?

【问题讨论】:

这可能会在codereview.stackexchange.com 上得到更好的接收。请记住,在使用 EF 上下文的 linq 语句中执行的任何操作都将转换为 SQL。因此,如果您认为在 sql server 之外执行该操作会更好,那么我绝对建议将这些操作/操作保留在 linq-to-entities 语句之外 这就是我想要做的。我希望在 sql server 中处理它,但我无法到达那里。这就是我寻求帮助的原因。 @JNYRanger 这段代码中哪个变量是你的 EF 上下文?是数据管理器吗?另外,您使用的是延迟加载还是急切加载? @JNYRanger - 是的,DataManager 是上下文的子类。我渴望加载子表(保险报价,所以会话,位置,司机,车辆,运营商等)。这就是为什么我需要避免过早的 .ToList() 【参考方案1】:

我刚刚解决了这个问题。这是给需要它的其他人的:

        if (!String.IsNullOrWhiteSpace(model.DrillDown))
        
            switch (model.SelectedInterval)
            
                case TimeInterval.Weekly:
                    model.FromDate = DateTime.ParseExact(model.DrillDown,"yyyy/MM/dd",CultureInfo.InvariantCulture);
                    model.ThroughDate = model.FromDate.AddDays(6);
                    break;
                case TimeInterval.Monthly:
                    model.FromDate = DateTime.ParseExact(model.DrillDown+"/01", "yyyy/MM/dd", CultureInfo.InvariantCulture);
                    model.ThroughDate = model.FromDate.AddMonths(1).AddDays(-1);
                    break;
                case TimeInterval.Annual:
                    model.FromDate = DateTime.ParseExact(model.DrillDown + "/01/01", "yyyy/MM/dd", CultureInfo.InvariantCulture);
                    model.ThroughDate = model.FromDate.AddYears(1).AddDays(-1);
                    break;
                case TimeInterval.Daily:
                    model.FromDate = DateTime.ParseExact(model.DrillDown, "yyyy/MM/dd", CultureInfo.InvariantCulture);
                    model.ThroughDate = model.FromDate;
                    break;
            

        

        var through = model.ThroughDate.AddDays(1);

        var drilldownQuery = DataManager.DataSessions
            .Include("Location")
            .Include("Quote.Carriers")
            .Include("Drivers")
            .Include("Vehicles")
            .Where(session =>
                session.Timestamp >= model.FromDate &&
                session.Timestamp < through
            );

        if (!String.IsNullOrWhiteSpace(model.DrillDown))
        
            switch (model.SelectedInterval)
            
                case TimeInterval.Hourly:
                    int selectedHour = int.Parse(model.DrillDown);
                    drilldownQuery = drilldownQuery.Where(session => session.Timestamp.Hour == selectedHour);
                    break;
                case TimeInterval.Weekday:
                    var selectedWeekday = (DayOfWeek) int.Parse(model.DrillDown);
                    drilldownQuery =
                        drilldownQuery.Where(
                            session => session.Timestamp.DayOfWeek == selectedWeekday);
                    break;
            
        
        return drilldownQuery;

    

【讨论】:

哈哈我正要发表评论,建议做一些非常相似的事情,但你打败了我。干得好!

以上是关于Linq to Entities 日期部分优化的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to Entities 格式化日期异常 [重复]

LINQ to Entities Perf优化

LINQ to Entities 异常中不支持指定的类型成员“日期”

LINQ to Entities 不支持指定的类型成员“日期”。 DbFunctions.TruncateTime()

在LINQ to Entities查询中调用自定义标量数据库函数?

将日期时间转换为 LINQ-to-entities 查询中的格式化字符串