我正在尝试从客户端构建来自表格网格的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()
调用之前不应该执行或评估查询,因此应该考虑排序和take / skip,但它不是:
{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,因为我只有两个可排序的列。
答案 0 :(得分:1)
logs.Skip(start).Take(length);
创建IQueryable<T>
,其中T
是相同的匿名类型,logs
是IQueryable<T>
但跳过start
项。然后,它会创建一个类似的IQueryable<T>
,其中lenght
个项目将是最多的。
然后它扔掉它并让它被垃圾收集。 (或者理想情况下,编译器或抖动步骤会意识到它被抛弃并切断整个事物)。
然后logs.ToList()
返回到您仍然拥有的logs
并从中创建一个列表。
您应该将Skip
和Take
行替换为:
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()
方法)允许await
。
ToList()
很少是最好的选择。