Linq查询使用Func &OrderByDescending
本文关键字:OrderByDescending Func 查询 Linq | 更新日期: 2023-09-27 18:17:22
考虑以下代码:
public List<Clients> GetFilteredClients(DateTime? FromDate = null,
DateTime? ToDate = null,
int? fromLocationType = null,
int? toLocationType = null)
{
Func<Clients, bool> fromDateFilter = f => true;
if (FromDate.HasValue)
{
fromDateFilter = z => z.Insert_Date.Value.Date >= FromDate.Value.Date;
}
Func<Clients, bool> toDateFilter = f => true;
if (ToDate.HasValue)
{
toDateFilter = z => z.Insert_Date.Value.Date <= ToDate.Value.Date;
}
Func<Clients, bool> fromLocationTypeFilter = f => true;
if (fromLocationType.HasValue)
{
fromOrgFilter = z => z.LocationTypeId >= fromLocationType.Value;
}
Func<Clients, bool> toLocationTypeFilter = f => true;
if (toLocationType.HasValue)
{
toLocationTypeFilter = z => z.LocationTypeId <= toLocationType.Value;
}
var filtered = DB_Context.Clients
.Where(fromDateFilter)
.Where(toDateFilter)
.Where(fromLocationTypeFilter)
.Where(toLocationTypeFilter)
.OrderByDescending(k => k.Id)
.Take(1000)
.ToList();
return filtered;
}
我在DB中有大约100K条记录,我只需要回答以下要求的前1000条:
.Where(fromDateFilter)
.Where(toDateFilter)
.Where(fromLocationTypeFilter)
.Where(toLocationTypeFilter)
但是执行时间仍然需要大约10秒。
知道为什么吗?
您必须使用Expression<Func<...>>
而不是Func<...>
。当您使用Func
时,只能在可查询对象上使用枚举方法,在这种情况下,这意味着您首先将所有内容下载到内存中,然后进行过滤。如果切换到Expression<...>
, O/RM将在DB服务器上进行过滤,而不是在您的应用程序中。
而且,有更好的方法来做你正在做的事情。例如,您可以构建这样的条件:
var query = DB_Context.Clients.AsQueryable();
if (FromDate.HasValue) query = query.Where(...);
if (ToDate.HasValue) query = query.Where(...);
...
return query.OrderByDescending(k => k.Id).Take(1000).ToList();
当然,这意味着无论您使用什么DB提供程序,都必须能够支持您尝试执行的过滤类型—您需要查阅文档。
您正在使用委托而不是LINQ表达式。这将导致你的应用程序处理数据,而不是SQL Server。
LINQ表达式看起来像lambda表达式感谢语法,但它们不是同一件事。编译器根据具体情况决定创建什么(委托或LINQ表达式)。
如果一个对象实现了IQueriable
接口,那么编译器将使用Queryable
类并生成LINQ表达式树,这些表达式树随后可以由特定的IQueryProvider
转换成SQL查询或其他形式。
否则,编译器使用Enumerable
类的扩展,它在源集合(在您的例子中是表中的所有记录)上创建迭代器。
为例。下面的代码将被编译成LINQ表达式。
// Source code
IQueryable<Clients> source = null;
IQueryable<Clients> result = source.Where(c => c.LocationTypeId >= 1);
// Compiller generated code
IQueryable<Clients> source = null;
Expression parameterC = Expression.Parameter(typeof(Clients), "c");
IQueryable<Clients> result = Queryable.Where<Clients>(
source,
Expression.Lambda<Func<Clients, bool>>(
Expression.LessThanOrEqual(
Expression.Property(
parameterC ,
typeof(Clients).GetProperty("LocationTypeId").GetGetMethod()
),
Expression.Constant(1, typeof(int))
),
new ParameterExpression[]
{
parameterC
}
);
下面的代码使用委托:
// Source code
IQueryable<Clients> source = null;
Func<Clients, bool> filter = c => c.LocationTypeId >= 1;
IEnumerable<Clients> result = source.Where(filter );
// Compiller generated code
IQueryable<Clients> source = null;
Func<Clients, bool> filter = c => c.LocationTypeId >= 1;
IEnumerable<Clients> result = Enumerable.Where(source, filter);
所以,为了解决你的问题,使用Expression<Func<Clients, bool>>
代替Func<Clients, bool>
:
IQueryable<Clients> result = DB_Context.Clients;
if (someFilter.HasValue)
result = result.Where(c => c.SomeProperty == someFilter.Value);
// other filters
return query
.OrderByDescending(k => k.Id)
.Take(1000)
.ToList();