我正在尝试使用Linq2Sql返回包含字符串列表中的值的所有行。 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);
}
关于如何实现这一目标的任何帮助或想法?
答案 0 :(得分: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中是可能的(也许它可以在这里或那里进行优化,但我认为它不会变得完美)。
我强烈建议将该函数的逻辑交换到数据库服务器的存储过程中。一个查询,由数据库服务器优化,不浪费内存。
另一种选择是重新考虑您的数据库设计。如果要查询一个字段的内容(您将此字段视为一个关键字数组,由空格分隔),您可能只是选择了不适当的数据库设计。您宁愿创建一个带有表的外键的新表。新表只有一个关键字。查询将更多更简单,更快速,更易理解。
答案 1 :(得分:0)
我没有尝试过,但如果我没记错的话,这应该可行:
from t in ctx.Table
where list.Any(x => t.MyProperty.Contains(x))
select t
如果您希望Any()
中的所有字符串匹配,则可以将All()
替换为list
修改强>
为了澄清我试图对此做些什么,这里是一个没有linq编写的类似查询,用于解释All
和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])