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秒。

知道为什么吗?

Linq查询使用Func &OrderByDescending

您必须使用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();
相关文章:
  • 没有找到相关文章