【问题标题】:Building LINQ Expression Getting Ignored构建 LINQ 表达式被忽略
【发布时间】:2015-06-21 00:07:19
【问题描述】:

我正在尝试从客户端的表格网格构建一个 linq 查询,所以我期待 页面偏移、页面开始、顺序和传统的分页参数。我有以下代码:

  [Route("api/settings/logs")]
   public Rest.DatatablesResponse GetLogs(int draw, int start, int length) {

       var query_string = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
       var search = query_string["search.value"];

       int order_column = int.Parse(query_string["order[0].column"]);
       var order_direction = query_string["order[0].dir"];

       var count = db.Logs.Count(q => q.Mode == 2);
       var logs = (from l in db.Logs
                   where l.Mode == 2
                   select new {
                       id = l.ID,
                       mode = l.Mode,
                       phase_id = l.Phase.ID,
                       created = l.Created,
                       user = l.User.Name,
                       blender_name = l.Blender.Name,
                       oil_name = l.Oil,
                       oil_quantity = l.OilQuantity,
                       production_cycle_name = l.ProductionCycle.Name
                   });

       if (order_direction == "asc") {
           if (order_column == 0) logs.OrderBy(q => q.created);
           else if (order_column == 2) logs.OrderBy(q => q.production_cycle_name);
       } else {
           if (order_column == 0) logs.OrderByDescending(q => q.created);
           else if (order_column == 2) logs.OrderByDescending(q => q.production_cycle_name);
       };

       if (!string.IsNullOrEmpty(search)) {
           logs.Where(q => q.blender_name.Contains(search) ||
                      q.oil_name.Contains(search) ||
                      SqlFunctions.StringConvert((decimal)q.id).Contains(search));
       }

      logs.Skip(start).Take(length);




        DateTime dtDateTime = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc);


        var steps = from l in logs.ToList()
                   select new {
                      id = l.id,
                      message = StringHelpers.FormatWith(_tpl_message[l.phase_id.ToString() +  l.mode.ToString() ], l) ,
                      created = dtDateTime.AddSeconds(l.created).ToString("h:mmtt - MMMM d, yyyy"),     
                      production_cycle_name = l.production_cycle_name
                   };

       return new Rest.DatatablesResponse {
           draw = draw,
           recordsTotal = count,
           recordsFiltered = count,
           data = steps.ToArray()
       };
   }

我的问题是 skip 和 take 和 orderby 表达式由于某种原因被忽略了,这是在将我的 linq 表达式转换为列表之前生成的 SQL 代码。据我了解,在我调用logs.ToList() 之前不应执行或评估查询,因此应考虑排序和采取/跳过,但事实并非如此:

{SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Mode] AS [Mode], 
    [Extent1].[Phase_ID] AS [Phase_ID], 
    [Extent1].[Created] AS [Created], 
    [Extent2].[Name] AS [Name], 
    [Extent3].[Name] AS [Name1], 
    [Extent1].[Oil] AS [Oil], 
    [Extent1].[OilQuantity] AS [OilQuantity], 
    [Extent4].[Name] AS [Name2]
    FROM    [dbo].[Steps] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[User_Id] = [Extent2].[Id]
    LEFT OUTER JOIN [dbo].[Blenders] AS [Extent3] ON [Extent1].[Blender_ID] = [Extent3].[ID]
    LEFT OUTER JOIN [dbo].[ProductionCycles] AS [Extent4] ON [Extent1].[ProductionCycle_ID] = [Extent4].[ID]
    WHERE 2 = [Extent1].[Mode]}

不相关的 P.S. 我使用不太聪明的 ifs 来构建订单表达式,而不是使用 DynamicLINQ,因为我只有两个可排序的列。

【问题讨论】:

    标签: linq


    【解决方案1】:
    logs.Skip(start).Take(length);
    

    创建一个IQueryable<T>,其中TlogsIQueryable<T> 相同的匿名类型,但跳过了start 项。然后,它会创建一个类似类型的 IQueryable<T>,其中 lenght 项目将被占用最多。

    然后它把它扔掉,让它被垃圾收集。 (或者理想情况下,编译器或抖动步骤会意识到它已被丢弃并删除整个内容)。

    然后logs.ToList() 回到您仍然拥有的logs 并从中创建一个列表。

    您应该将 SkipTake 行替换为:

    logs = logs.Skip(start).Take(length);
    

    所以你实际上是在利用这种跳过和采取。

    我使用不太聪明的 ifs 来构建订单表达式,而不是使用 DynamicLINQ,因为我只有两个可排序的列。

    除了你犯了同样的错误之外,没有什么特别不聪明的;应用 OrderBy 然后丢弃结果而不是使用它。 Where 也是如此。你需要logs = logs.OrderBy(...)等。

    我也会在这里提问from l in logs.ToList() select new {…}

    如果一步获得该列表有一些优势,这可能是最好的方法。否则:

    from l in logs select new {…}
    

    对数据库执行 select 操作,检索您需要的内容。

    from l in logs.AsEnumerable() select new {…}
    

    在应用程序中执行select 工作,如果其中一部分无法转换为数据库工作,则适当,但应按原样进行,而不是先将其全部加载到内存中。

    from l in await logs.ToListAsync() select new {…}
    

    ToList() 的缺点,但在异步使用中,那么(假设您的提供程序有ToListAsync() 方法)允许awaiting。

    ToList() 很少是这里的最佳选择。

    【讨论】:

    • 非常感谢,尽管 .Skip 的工作方式类似于 .Where 它不会创建新实例。我还没有在我的代码中包含整个异步的东西,但看起来我很快就会从你的回答中得到答案
    • Skip() 的工作方式类似于 .Where(),它确实创建了一个新实例。所有这些方法都会创建一个新的轻量级实例,该实例本身是不可变的,因此无法更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多