繁体   English   中英

使用Func和OrderByDescending时Linq查询花费的时间太长

[英]Linq query takes too long when using Func & OrderByDescending

考虑以下代码:

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;
}

我在数据库中有100K条记录,我只需要满足以下要求的前1000条记录:

.Where(fromDateFilter)
.Where(toDateFilter)
.Where(fromLocationTypeFilter)
.Where(toLocationTypeFilter)

但是执行时间仍然需要大约10秒。

知道为什么吗?

您必须使用Expression<Func<...>>而不是Func<...> 使用Func ,仅可枚举方法可用于查询对象,在这种情况下,这意味着您首先将所有内容下载到内存中,然后进行过滤。 如果切换到Expression<...> ,则O / RM将在数据库服务器上而不是在应用程序中进行过滤。

此外,还有更好的方法来做您正在做的事情。 例如,您可以建立如下条件:

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();

当然,这意味着您正在使用的任何数据库提供程序都必须能够支持您尝试执行的过滤类型-您需要查阅文档。

您正在使用委托而不是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();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM