延迟执行与ToList给出不同的结果
本文关键字:结果 执行 ToList 延迟 | 更新日期: 2023-09-27 18:05:19
我正在单元测试一个为搜索可查询项而编写的函数。我只是断言我得到了1个项,如果该方法有效,我应该得到它。但我拿回了0个项目。在我的方法中,我使用延迟执行,在返回之前只使用ToList
。但如果我改为直接使用列表并重复调用ToList
,我会得到正确的结果。
我是否正确地说,假设延迟执行产生与立即执行相同的结果是不安全的?
这是一个小应用程序,用于证明它返回0个项目
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> values = new Dictionary<string, string>()
{
{
"Prop1",
"*Value*"
},
{
"Prop2",
"2*"
}
};
List<InputItem> items =new List<InputItem>()
{
new InputItem()
};
Console.WriteLine(Helper.SearchInputItems(items.AsQueryable(), values).Count);
Console.ReadLine();
}
}
public class InputItem
{
public Dictionary<string, string> MappedValues = new Dictionary<string, string>()
{
{
"Prop1",
"This is a value that should be found"
},
{
"Prop2",
"2 everything that begins with 2 should be found"
}
};
}
public static class Helper
{
delegate bool Searcher(string input, string searchString);
/// <summary>
/// Searches the added input items.
/// </summary>
/// <param name="values">A dictionary of field names and the search pattern for that field.</param>
/// <returns>List of found InputItems.</returns>
public static List<InputItem> SearchInputItems(IQueryable<InputItem> inputItems, Dictionary<string, string> values)
{
foreach (var value in values)
{
string searchString = value.Value;
Searcher searcher;
if (searchString.StartsWith("*") && searchString.EndsWith("*"))
{
searcher = new Searcher(StringHelpers.Contains);
searchString = searchString.Substring(1);
searchString = searchString.Remove(searchString.Length - 1);
}
else if (searchString.EndsWith("*"))
{
searcher = new Searcher(StringHelpers.StartsWith);
searchString = searchString.Remove(searchString.Length - 1);
}
else
{
searcher = new Searcher(StringHelpers.Exact);
}
inputItems = inputItems.Where(c =>
c.MappedValues.Any(x => x.Key == value.Key) &&
searcher(c.MappedValues.First(x => x.Key == value.Key).Value, searchString)
);
}
return inputItems.ToList();
}
}
public static class StringHelpers
{
public static bool Contains(string input, string searchString)
{
return input.ToUpperInvariant().Contains(searchString.ToUpperInvariant());
}
public static bool StartsWith(string input, string searchString)
{
return input.ToUpperInvariant().StartsWith(searchString.ToUpperInvariant());
}
public static bool Exact(string input, string searchString)
{
return input.ToUpperInvariant() == searchString.ToUpperInvariant();
}
}
如果我设置了一个断点,我实际上可以看到它检查2 everything that begins with 2 should be found
是否包含Value
,但它没有,并返回false。因此,where子句中的FirstOrDefault似乎选择了错误的项
好吧,我明白了。这是捕获循环变量的老问题。
此处:
foreach (var value in values)
{
...
inputItems = inputItems.Where(c =>
c.MappedValues.Any(x => x.Key == value.Key) &&
searcher(c.MappedValues.First(x => x.Key == value.Key).Value,
searchString)
);
}
您在lambda表达式中使用value
,这意味着它在执行时将使用"value
的当前值"。。。并且该值随着循环的迭代而改变。
简单使用:
foreach (var valueIterationVariable in values)
{
var value = valueIterationVariable;
// code as before
}
我相信一切都会好起来的。(顺便说一句,我会质疑你使用"value"这个名字,但那是另一回事。(
我还没有深入研究为什么它与IEnumerable<T>
一起工作,而与IQueryable<T>
不一起工作,但我怀疑是额外的延迟造成的。