我们有一个AdvancedSearchModel
,它只是一个包含多个字符串的结构。 SerialNumbers
是一串用逗号分隔的序列号(长度为10个字符)。这被分成一个字符串数组。我们正在搜索一个包含Devices
列的SerialNumber
表。我们还需要部分匹配这些搜索词组,这解释了Any
和Contains
用法。 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个字符串数组,但我不确定这真的有帮助...
有什么想法吗?
答案 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);
当然,批处理也是如此。但是,对于这么小的论证集,它不应该是必要的。