如何“拆分”或优化Linq to Sql查询。

时间:2014-04-08 21:51:44

标签: c# linq entity-framework linq-to-sql

背景

我们有一个AdvancedSearchModel,它只是一个包含多个字符串的结构。 SerialNumbers是一串用逗号分隔的序列号(长度为10个字符)。这被分成一个字符串数组。我们正在搜索一个包含Devices列的SerialNumber表。我们还需要部分匹配这些搜索词组,这解释了AnyContains用法。 AdvancedSearchModel中大约有10个字符串,我们可以毫无问题地将每个字符串应用于查询。

public static IQueryable<Device> ApplySearchQuery(IQueryable<Device> query, AdvancedSearchModel searchModel)
{
    if (!String.IsNullOrWhiteSpace(searchModel.SerialNumbers))
    {
        string[] serialNumbers = searchModel.SerialNumbers.Split(',');
        query = query.Where(d => serialNumbers.Any(s => d.SerialNumber.Contains(s))); 

     }
     // ... more queries added that are part of the search model
}

问题

让我先说明这一点,实施时,我们预计最多可以搜索大约10个序列号。 然而,我们发现用户最多可以输入100个序列号。我们得到一个错误,即查询与此嵌套太深。因此,即使我们只按SerialNumbers进行搜索,当serialNumbers增长到43个元素以上时,它也会抛出异常。

我不确定如何分割这个查询,因为它们很简单(尽管我们期望结果很慢)。

也许我们可以ArrayList string[]大小~25,并且增长此数组列表,即如果用户搜索100个序列号,那么ArrayList将有4个字符串数组,但我不确定这真的有帮助...

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

我想如果你手动生成OR表达式,你可以让EF生成一个扁平的OR语句而不是嵌套的子查询:

string[] serialNumbers = searchModel.SerialNumbers.Split(',');
// GetMethod defined at http://www.codeducky.org/10-utilities-c-developers-should-know-part-two/
var stringContainsMethod = Helpers.GetMethod((string s) => s.Contains(default(string)));
var serialNumberProperty = Helpers.GetProperty((Device d) => d.SerialNumber);
var parameter = Expression.Parameter(typeof(Device));

// generate a condition d.SerialNumber.Contains(sn) for each sn
var conditions = serialNumbers.Select(sn => Expression.Call(  
    Expression.MakeMemberAccess(parameter, serialNumberProperty), // d.SerialNumber
    stringContainsMethod, // the method to invoke
    Expression.Constant(sn) // equivalent of a string literal for the serial number
));

// generate a lambda we can pass to Where: 
// d => d.SerialNumber.Contains(sn1) || d.SerialNumber.Contains(sn1) || ..
var predicate = Expression.Lambda<Func<Device, bool>>(
    // merge each condition with ORs
    conditions.Aggregate<Expression>((c1, c2) => Expression.OrElse(c1, c2)),
    parameter
);

// apply the predicate
query = query.Where(predicate);

当然,批处理也是如此。但是,对于这么小的论证集,它不应该是必要的。