2010-04-11 54 views
3

我想使用Linq2Sql返回包含字符串列表中的值的所有行。 linq2sql类对象具有包含由空格分隔的单词的字符串属性。c#使用Linq2Sql查找表列中的匹配词

public class MyObject 
{ 
    public string MyProperty { get; set; } 
} 

例myProperty的值是:

MyObject1.MyProperty = "text1 text2 text3 text4" 
MyObject2.MyProperty = "text2" 

例如,使用一个字符串集合,我通过以下列表

var list = new List<>() { "text2", "text4" } 

这将在我的例子返回上面两个项目,他们都包含“text2”值。

但是,我尝试了下面的代码,因为我的扩展方法无法评估Linq2Sql。

public static IQueryable<MyObject> WithProperty(this IQueryable<MyProperty> qry, 
    IList<string> p) 
{ 
    return from t in qry 
     where t.MyProperty.Contains(p, ' ') 
     select t; 
} 

我也写了一个扩展方法

public static bool Contains(this string str, IList<string> list, char seperator) 
{ 
    if (str == null) return false; 
    if (list == null) return true; 

    var splitStr = str.Split(new char[] { seperator }, 
     StringSplitOptions.RemoveEmptyEntries); 

    bool retval = false; 
    int matches = 0; 

    foreach (string s in splitStr) 
    { 
     foreach (string l in list) 
     { 
      if (String.Compare(s, l, true) == 0) 
      { 
       retval = true; 
       matches++; 
      } 
     } 
    } 

    return retval && (splitStr.Length > 0) && (list.Count == matches); 
} 

我如何能做到这一点任何帮助或想法?

回答

1

特殊照顾。扩展方法WithProperty的第一个参数必须为IQueryable<MyObject>类型,而不是IQueryable<MyProperty>

无论如何,你不需要扩展方法的IQueryable。只需在lambda中使用Contains方法进行过滤。这应该工作:

List<string> searchStrs = new List<string>() { "text2", "text4" } 

IEnumerable<MyObject> myFilteredObjects = dataContext.MyObjects 
        .Where(myObj => myObj.MyProperty.Contains(searchStrs, ' ')); 

更新:

上面的代码片段不会工作。这是因为Contains方法不能转换为SQL语句。我想了一会儿问题,并通过考虑'我将如何在SQL中做到这一点'来解决问题:您可以通过查询每个关键字并将所有结果联合在一起来实现。令人遗憾的是,延迟执行Linq-to-SQL可以防止在一个查询中执行这些操作。所以我想出了妥协的妥协方案。它查询每一个关键字。这可以是以下之一:

  • 等于字符串
  • 两个分隔符
  • 在字符串的开始和随后之间由分隔符
  • 或在字符串的末尾和由分隔符组成

这跨越了一个有效的表达式树,并且可以通过Linq-to-SQL转换成SQL。查询后,我不推迟执行,立即获取数据并将其存储在列表中。所有列表之后都会合并。

public static IEnumerable<MyObject> ContainsOneOfTheseKeywords(
     this IQueryable<MyObject> qry, List<string> keywords, char sep) 
{ 
    List<List<MyObject>> parts = new List<List<MyObject>>(); 

    foreach (string keyw in keywords) 
     parts.Add((
      from obj in qry 
      where obj.MyProperty == keyw || 
        obj.MyProperty.IndexOf(sep + keyw + sep) != -1 || 
        obj.MyProperty.IndexOf(keyw + sep) >= 0 || 
        obj.MyProperty.IndexOf(sep + keyw) == 
         obj.MyProperty.Length - keyw.Length - 1 
      select obj).ToList()); 

    IEnumerable<MyObject> union = null; 
    bool first = true; 
    foreach (List<MyObject> part in parts) 
    { 
     if (first) 
     { 
      union = part; 
      first = false; 
     } 
     else 
      union = union.Union(part); 
    } 

    return union.ToList(); 
} 

并使用它:

List<string> searchStrs = new List<string>() { "text2", "text4" }; 

IEnumerable<MyObject> myFilteredObjects = dataContext.MyObjects 
        .ContainsOneOfTheseKeywords(searchStrs, ' '); 

这种解决方案确实比一切其他优雅。对于10个关键字,我必须查询数据库10次,每次捕获数据并将其存储在内存中。这是在浪费记忆,并且表现不佳。我只是想证明它可能在Linq中(也许它可以在这里或那里进行优化,但我认为它不会完美)。

我强烈建议将该函数的逻辑交换为数据库服务器的存储过程。一个查询,由数据库服务器优化,并且不浪费内存。

另一种选择是重新考虑你的数据库设计。如果您想要查询一个字段的内容(您将这个字段当作关键字数组,用空格分隔),您可能只是选择了不适当的数据库设计。您更希望使用您的表的外键创建一个新表。新表格只有一个关键字。查询将是太多更简单,更快,更容易理解。

+0

没有抱歉,这不起作用。我的自定义Contains方法无效(这只是解释我在做什么的一个例子),因为它不能被赋值,因为它不能被转换为SQL。 – David 2010-04-11 11:54:14

+0

你说得对,我更新了答案。 – 2010-04-11 14:18:26

+0

第二个被剪切的代码现在可以正常工作,只需使用示例数据库对其进行测试即可。 – 2010-04-11 14:20:34

0

我没有试过,但如果我没有记错,这应该工作:

from t in ctx.Table 
where list.Any(x => t.MyProperty.Contains(x)) 
select t 

可以用All()取代Any()如果你想在list所有字符串匹配

编辑:

为了澄清我正在试图用这个做什么,这里是一个类似的查询没有linq编写,以解释使用All a第二Any

where list.Any(x => t.MyProperty.Contains(x)) 

翻译为:

where t.MyProperty.Contains(list[0]) || t.MyProperty.Contains(list[1]) || 
     t.MyProperty.Contains(list[n]) 

而且

where list.Any(x => t.MyProperty.Contains(x)) 

翻译为:在正确的轨道上

where t.MyProperty.Contains(list[0]) && t.MyProperty.Contains(list[1]) && 
     t.MyProperty.Contains(list[n]) 
+1

我不确定这是否合理,因为MyProperty包含一个完整的字符串“text1 text2 text3”,需要将其分解为单独的值,然后与列表进行比较。我试过你的建议,并得到以下错误:除了包含()运算符 – David 2010-04-11 11:44:24

+0

您的答案与问题无关!本地序列不能用于查询运算符的LINQ to SQL实现!它不起作用,Any和All操作符完成与OP想要的完全不同的操作。 – 2010-04-11 14:26:31

+0

@phild:OP已经说过它不起作用,但Any和All操作符在使用这种方式时是正确的。在我的回答中看到我的解释。如果不是他提到的错误,这种方式只适用于 – 2010-04-12 07:26:00