我有一个字符串:
strCheckedCategories = "2;"
表示 SharePoint 列表的实体列表,其项 ID 从 1 到 21:
EntityList<VendorSearchesItem> vendorSearches =
dataContext.GetList<VendorSearchesItem>("Vendor Searches");
一个 LINQ 查询,从联接到"供应商搜索"列表的两个 SharePoint 列表中返回字段:
var vendorSearchesQuery = (from s in vendorSearches
orderby s.VendorID.Title
select new
{
Vendor = s.VendorID.Title,
Website = s.VendorID.VendorWebsite,
VendorID = s.VendorID.Id,
SearchType = s.SearchTypeID.Title,
SearchTypeId = s.SearchTypeID.Id
});
另一个 LINQ 查询仅返回项 ID 在列表中的项:
var q2 = from m2 in vendorSearchesQuery
where strCheckedCategories.Contains(m2.SearchTypeId.ToString())
select m2
问题是,除了返回 ID 为 2(所需结果)的项目外,查询还返回 ID 为 12、20 和 21 的项目。我该如何解决这个问题?
因此,从根本上说,您要在这里做的是有一个IN
子句,您可以在其中为字段指定一堆值,并且您希望该列的值在该集合中的行。
CAML 实际上确实有一个您可以使用的 IN 子句,但遗憾的是,LINQ to SharePoint 不提供任何生成 IN 子句的方法;查询提供程序根本不支持它。
您正在尝试通过尝试进行字符串比较而不是使用正确的运算符来解决此问题,并且您遇到了将所有操作串化的问题。 它根本不适合这项任务。
正如我所说,由于您无法让 LINQ to SharePoint 使用 IN,因此一种选择只是不使用 LINQ,手动生成 CAML,然后使用标准服务器对象模型执行它。 但这并不好玩。
我们可以做的是进行一系列OR检查。 我们将查看该列值是集合中所有值的第一个值、第二个值还是第三个值等。 这实际上与 IN 子句相同,只是更冗长。
现在,这就引出了如何将未知数量的比较放在一起的问题。 如果是 AND,那很容易,我们只需在循环中调用 Where
,它就会和那些 N 子句。
相反,我们需要使用表达式。 我们可以自己手动构建动态数量的 OR 子句的表达式树,然后查询提供程序将能够很好地解析它。
我们的新方法 WhereIn
将查询过滤到给定属性值位于一组值中的所有项,需要接受查询、我们正在使用的属性的属性选择器以及一组相同类型的值进行比较。 有了这个之后,一个简单的问题就是创建属性访问的比较表达式以及每个键值,然后对所有这些表达式进行 ORing。
public static IQueryable<TSource> WhereIn<TSource, TKey>(
this IQueryable<TSource> query,
Expression<Func<TSource, TKey>> propertySelector,
IEnumerable<TKey> values)
{
var t = Expression.Parameter(typeof(TSource));
Expression body = Expression.Constant(false);
var propertyName = ((MemberExpression)propertySelector.Body).Member.Name;
foreach (var value in values)
{
body = Expression.OrElse(body,
Expression.Equal(Expression.Property(t, propertyName),
Expression.Constant(value)));
}
return query.Where(Expression.Lambda<Func<TSource, bool>>(body, t));
}
现在要调用它,我们只需要查询、我们正在过滤的属性和值集合:
var q2 = vendorSearchesQuery.WhereIn(vendor => vendor.SearchTypeId
, strCheckedCategories.Split(';'));
瞧。
虽然我希望它按原样工作,但您可能需要在Select
之前致电WhereIn
。 它可能不适用于已经映射的SearchTypeId
。
您可能应该使用正则表达式,但是如果您想要更简单的解决方案,那么我将避免字符串搜索并将这些字符串拆分为数组:
string strCheckedCategories = "2;4;5;7;12;16;17;19;20;21;";
string[] split = strCheckedCategories.Split(';');
它将在数组中为尾随分号分隔符创建一个空条目。 如果这是一个问题,我会检查并删除它:
strCheckedCategories.TrimEnd(';');
最后,现在您可以更改where
子句:
where split.Contains(m2.SearchTypeId.ToString())
如果你有一个非常大的列表,那么通过将strCheckedCategories解析为整数列表来比较整数而不是字符串可能是值得的:
int[] split = strCheckedCategories.Split(';').Select(x => Convert.ToInt32(x)).ToArray();
然后,您可以执行更快的相等表达式:
where split.Contains(m2.SearchTypeId)
尝试:
strCheckedCategories.Split(new []{';'}).Any(x => x == m2.SearchTypeId.ToString())
包含将执行子字符串匹配。"20"有一个子字符串"2"。
var q2 = from m2 in vendorSearchesQuery
where strCheckedCategories.Split(';').Contains(m2.SearchTypeId.ToString())
select m2
var q2 = from m2 in vendorSearchesQuery
where strCheckedCategories.Contains(";" + m2.SearchTypeId + ";")
select m2
并且您的strCheckedCategories
应始终以;
结尾并以;
开头,例如;2;
,;2;3;
,...
注意:仅当您的SearchTypeId
始终不应包含;
时,此技巧才有效。我认为您应该使用另一种分隔符,例如n
,或者只是将选中的类别存储在列表或某个数组中。这是更标准的做法。