LINQ - 将字符串列表与冒号分隔的字段进行比较

时间:2013-01-30 11:51:17

标签: c# linq

是否可以执行LINQ where子句并将a上的varchar字段拆分为一个集合,并将其与另一个集合进行比较,以查看是否有任何值匹配。

例如,我们有一个名为AllowedHolders的List<string>,其中包含ARR ACC等。但是数据库中的字段(,遗憾的是我们无法更改)是一个varchar但是值已分隔通过冒号:即“:ARR:ACC:”

我们想要做的是写一个LINQ where子句,它可以检查是否有任何AllowedHolders出现在字段中。我们希望将结果限制为仅返回字段在AllowedHolders集合中包含值的记录。

我们已完成以下操作,其中字段仅包含单个值

searchResults = searchResults.Where(S => searchParams.AllowedBusinessAreas.Contains(S.SIT_BusinessArea));

但是以下内容不起作用,因为SIT_HolderNames包含以冒号分隔的值:

searchResults = searchResults.Where(S => searchParams.AllowedHolders.Contains(S.SIT_HolderName)

任何想法都会非常感激。如果您需要我进一步解释,请告诉我。

安迪

3 个答案:

答案 0 :(得分:2)

使用Intersect()Any()String.Split()

searchResults = searchResults.Where(s => searchParams.AllowedHolders.Intersect(S.SIT_HolderName.Split(':')).Any());

例如:

":ACC:TEST::ARR:".Split(':') -> string[] { "", "ACC", "TEST", "", "ARR", "" };  

您可以注意到空字符串,如果您不想将它们考虑在内,请使用String.Split(char [],StringSplitOptions.RemoveEmptyEntries):

":ACC:TEST::ARR:".Split(new char[] {':'}, StringSplitOptions.RemoveEmptyEntries) -> string[] { "ACC", "TEST", "ARR" };

更新

在使用String.Split()调用ToList()之前,您必须获取数据。

searchResults = searchResults.ToList().Where(s => searchParams.AllowedHolders.Intersect(S.SIT_HolderName.Split(':')).Any());

如果searchResults中的数据太大,您只能获取主键和SIT_HolderName

var keys = searchResults.Select(s => new { Key = s.SIT_PKey, SIT_HolderName = s.SIT_HolderName })
                        .ToList()
                        .Where(s => searchParams.AllowedHolders.Intersect(s.SIT_HolderName.Split(':')).Any())
                        .Select(s => s.Key)
                        .ToList();

searchResult = searchResults.Where(s => keys.Contains(s.SIT_PKey));

我不知道上述查询的性能是什么。否则,您可以尝试使用Join()

searchResult = searchResults.Join(keys, s => s.SIT_PKey, key => key, (s, key) => s);

答案 1 :(得分:1)

也许你可以使用:

searchResults = searchResults.Where(S => searchParams.AllowedHolders
  .Any(H => S.SIT_HolderName.Contains(H))
  );

searchResults = searchResults.Where(S => searchParams.AllowedHolders
  .Any(S.SIT_HolderName.Contains)
  );

正如第一条评论所指出的,如果没有持有者名称包含另一个持有者名称作为子字符串,则此有效。我隐含地假设您的所有持有人姓名都是三个字母的字符串,如ARRACC。如果不是这种情况,请考虑使用(":" + H + ":"),或者找到更安全的解决方案。

编辑:为了完整起见,这里有两个版本,其中冒号前置并附加:

// OK if some name is contained in another name as a substring
// Requires colon before the first and after the last name
searchResults = searchResults.Where(S => searchParams.AllowedHolders
  .Any(H => S.SIT_HolderName.Contains(":" + H + ":"))
  );

// OK if some name is contained in another name as a substring
// Ugly checks included to handle cases where no colons are present in the extreme ends
searchResults = searchResults.Where(S => searchParams.AllowedHolders
  .Any(H => S.SIT_HolderName.Contains(":" + H + ":") || S.SIT_HolderName.StartsWith(H + ":") || S.SIT_HolderName.EndsWith(":" + H) || S.SIT_HolderName == H)
  );

答案 2 :(得分:0)

如果在DB列的值中,分隔符确实采用以下格式:

:AAA:BBB:CCC:DDD:

而不仅仅是(请注意第一个和最后一个角色!)

AAA:BBB:CCC:DDD

然后您可以执行LIKE查找:

select .... from ... where column LIKE '%:BBB:%'

转换为LINQ:

var idWithColons = ":" + "BBB" + ":";

from ... where .... column.Contains(idWithColons)

对于许多可能的IDS,您必须生成:

select .... from ... where column LIKE '%:BBB:%' OR column LIKE '%:DDD:%' OR ..

转换为LINQ:

var idWithColons = ":" + "BBB" + ":";
var idWithColons2 = ":" + "DDD" + ":";

from ... where .... column.Contains(idWithColons) or column.Contains(idWithColons2)

但这只适用于少数替代品。对于未知的ID列表,您可以尝试将其重写为动态构建的过滤器,但如果您不熟悉Expression<Func<>>,那就不那么容易了。无论如何,通过LIKE搜索并不是一个好主意..但是这不是很多其他选择:/

否则,那是不合适的..你可以在sql服务器上准备一个标量值函数,并以某种方式在你的linq提供程序中注册它,但我不认为这是值得的..

编辑:

动态构建where子句,即。这里http://www.albahari.com/nutshell/predicatebuilder.aspx - 寻找PredicateBuilder。构建器实际上是通用的并且可以直接使用,但是您仍然必须编写自己连接OR-LIKE的小循环。我认为这篇文章写得很好,如果你发现任何问题,请给我发消息。表演除外。 LIKE %%并不快。