2

我有一个字符串:

strCheckedCategories = "2;"

表示 SharePoint 列表的 EntityList,项目 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 的项目。我该如何解决这个问题?

4

5 回答 5

4

因此,从根本上说,您在这里要做的是有一个IN子句,在该子句中为一个字段指定一堆值,并且您希望该列的值在该集合中的行。

CAML 实际上有一个您可以使用的 IN 子句,但遗憾的是 LINQ to Sharepoint 没有提供任何生成 IN 子句的方法;查询提供程序根本不支持它。

您试图通过尝试进行字符串比较而不是使用正确的运算符来使用一些技巧来解决该问题,并且您遇到了将所有操作字符串化的陷阱。它根本不适合这项任务。

因为,正如我所说,您无法让 LINQ to SharePoint 使用 IN,所以一种选择就是不使用 LINQ,手动构建 CAML,并使用标准服务器对象模型执行它。但这并不好玩。

我们可以做的是进行一系列 OR 检查。我们将查看该列值是您集合中所有值的第一个值,还是第二个或第三个等。这实际上与 IN 子句相同,只是更加冗长。

现在这给我们带来了如何对未知数量的比较进行或运算的问题。如果它是 AND 就很容易了,我们只需Where在循环内部调用它就会 AND 那些 N 子句。

相反,我们需要使用表达式。我们可以自己手动构建动态数量的 OR 子句的表达式树,然后查询提供程序将能够很好地解析它。

我们的新方法WhereIn将查询过滤到给定属性值在一组值中的所有项目,它需要接受一个查询、一个我们正在使用的属性的属性选择器以及一组相同的类型来比较它。在我们拥有它之后,创建属性访问的比较表达式以及每个键值,然后对所有这些表达式进行 OR 运算就很简单了。

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

瞧。

虽然我希望它能够按原样工作,但您可能需要WhereIn Select. 它可能不适用于已经映射的SearchTypeId.

于 2013-10-11T16:42:19.173 回答
1

您可能应该使用正则表达式,但如果您想要一个更简单的解决方案,那么我会避免字符串搜索并将这些字符串拆分为一个数组:

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)
于 2013-10-11T16:29:20.640 回答
0
var q2 = from m2 in vendorSearchesQuery 
         where strCheckedCategories.Contains(";" + m2.SearchTypeId + ";")
         select m2

strCheckedCategories应该始终以 结束;和开始;,例如;2;, ;2;3;...

注意:这个技巧只有在你SearchTypeId不应该包含;. 我认为您应该使用另一种分隔符,例如\n简单地将选中的类别存储在列表或某个数组中。这是更标准的做法。

于 2013-10-11T16:31:03.777 回答
0

尝试:

strCheckedCategories.Split(new []{';'}).Any(x => x == m2.SearchTypeId.ToString())

包含将进行子字符串匹配。“20”有一个子串“2”。

于 2013-10-11T16:27:51.677 回答
0
var q2 = from m2 in vendorSearchesQuery 
         where strCheckedCategories.Split(';').Contains(m2.SearchTypeId.ToString())
         select m2
于 2013-10-11T16:28:40.360 回答