我有一个项目集合(ADO.NET实体框架),需要根据几个不同的条件返回一个子集作为搜索结果。不幸的是,条件重叠的方式使得我不能只获取满足条件的集合(或者丢弃Where Where条件不满足),因为这会遗漏或重复应该返回的有效项。
我决定单独进行每一次检查,并将结果组合在一起。我考虑过使用AddRange,但这会导致结果列表中出现重复(我的理解是每次它都会枚举集合-这里我是对还是错?)。我意识到Union不会插入重复项,并将枚举推迟到必要时(同样,这种理解正确吗?)。
搜索内容如下所示:
IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition
int parsed_key;
//For each searchable value
foreach(var selected in SelectedValues1)
{
IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
Results = Results.Union(matched); //This is where the problem is
}
//Ellipsed....
foreach(var selected in SelectedValuesN) //Happens to be integer
{
if(!int.TryParse(selected, out parsed_id))
continue;
IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
Results = Results.Union(matched); //This is where the problem is
}然而,看起来Results = Results.Union(matched)的工作方式更像Results = matched。我已经完成了一些测试数据和测试搜索。搜索请求第一个字段为-1、0、1或3的结果。这将返回4个结果(两个0、一个1和一个3)。循环的第一次迭代按预期工作,结果仍然为空。第二次迭代也像预期的那样工作,结果包含两个项目。然而,在第三次迭代之后,结果只包含一项。
我是不是误解了.Union的工作原理,还是有别的原因?
发布于 2012-08-13 22:08:14
由于延迟执行,当您最终使用Results时,它是许多Where查询的联合,所有这些查询都基于 selected的最后一个值。
所以你有
Results = Potential.Where(selected)
.Union(Potential.Where(selected))
.Union(potential.Where(selected))...并且所有的selected值都是相同的。
您需要在循环中创建一个var currentSelected = selected并将其传递给查询。这样一来,selected的每个值都将被单独捕获,您就不会遇到这个问题。
发布于 2012-08-13 22:05:51
您可以更简单地做到这一点:
Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));(这可能会返回重复项)
或
Results = Potential.Where(x => SelectedValues.Contains(x.Value));发布于 2012-08-13 22:39:47
正如其他人所指出的,您的LINQ表达式是一个closure。这意味着变量selected会在foreach循环的每次迭代中被LINQ表达式捕获。在foreach的每次迭代中都使用相同的变量,因此它最终将具有上一个值。为了解决这个问题,您需要在foreach循环中声明一个局部变量,如下所示:
//For each searchable value
foreach(var selected in SelectedValues1)
{
var localSelected = selected;
Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}只使用.Contains()要短得多
Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));因为您需要查询多个SelectedValues集合,所以可以将它们都放在它们自己的集合中并遍历它们,尽管您需要一些方法来匹配对象上的正确字段/属性。
您可以通过将所选值的列表以字段/属性的名称作为键存储在字典中来实现这一点。您将使用反射来查找正确的字段并执行检查。然后,可以将代码缩短为以下代码:
// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;
Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
// Try to get a property for this key
PropertyInfo prop = t.GetProperty(selectedValues.Key);
IEnumerable<MyClass> localSelected = selectedValues.Value;
if( prop != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(prop.GetValue(x, null))));
}
else // If it's not a property, check if the entry is for a field
{
FieldInfo field = t.GetField(selectedValues.Key);
if( field != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(field.GetValue(x, null))));
}
}
}https://stackoverflow.com/questions/11935897
复制相似问题