IDictionary 扩展实际上不是 运行 而在 LInq 查询中.Where()

IDictionary extension not actually running while in LInq Query .Where()

我有一个 IDictionary 的扩展,目的是让它在执行包含时不区分大小写:

public static bool ContainsKeyIgnoreCase<TKey, TModel>(this IDictionary<TKey, TModel> dictionary, TKey key)
{
    bool? keyExists;

    var keyString = key as string;
    if (keyString != null)
    {
        keyExists =
            dictionary.Keys.OfType<string>()
            .Any(k => string.Equals(k, keyString, StringComparison.OrdinalIgnoreCase));
    }
    else
    {
        keyExists = dictionary.ContainsKey(key);
    }

    return keyExists ?? false;
}

但它似乎从来没有 运行,在 .Where() :

中遇到断点或任何东西
public IEnumerable<TModel> PopulateIdsFromExistingRowsAndReturnNewRowsToCreate<TFromDBDto, TKey, TModel>(
    IEnumerable<TFromDBDto> fromDatabase,
    IDictionary<TKey, TModel> fromImport,
    Func<TFromDBDto, TKey> keySelector)
    where TFromDBDto : class, IGuidDataTransferObject
    where TModel : class, IModel<Guid>
{

    var groupedItems = fromDatabase
        .GroupBy(i => keySelector(i));
    var onlyKeys = groupedItems.Select(i => new { key = i.Key, value = i.First() });
    var filtered = onlyKeys.Where(i => fromImport.ContainsKeyIgnoreCase(i.key));
    var items = filtered.ToDictionary(i => i.key, i => i.value);

    var newItems = fromImport
        .Where(i => !items.ContainsKeyIgnoreCase(i.Key))
        .Select(i => i.Value);

    fromImport
        .Where(i => items.ContainsKeyIgnoreCase(i.Key))
        .ForEach(i => { i.Value.ID = items.AtKey(i.Key).ID; });

    return newItems;
}

然而,如果在 for 循环中,它将 运行:

foreach(var keys in onlyKeys)
    {
        fromImport.ContainsKeyIgnoreCase(keys.key);
    }

想知道我是不是做错了什么,或者只是为什么它会拒绝 运行 通过扩展。

没有任何处理查询:

fromImport
    .Where(i => items.ContainsKeyIgnoreCase(i.Key))
    .ForEach(i => { i.Value.ID = items.AtKey(i.Key).ID; });

ForEach() 扩展方法的定义是什么? 可能是 ForEach() 正在返回一个迭代器 (IEnumerable<>);你需要用它做一些事情来处理查询。例如:

var myStuff = fromImport
    .Where(i => items.ContainsKeyIgnoreCase(i.Key))
    .ForEach(i => { i.Value.ID = items.AtKey(i.Key).ID; })
    .ToList();  // <-- this will run the query

继续我的评论,正如@Kirk Woll 所指出的,这是一个 “延期执行”。简而言之,Where 方法的执行会延迟到您需要该数据时。

在你的例子中,你有以下代码:

    var newItems = fromImport
        .Where(i => !items.ContainsKeyIgnoreCase(i.Key))
        .Select(i => i.Value);

    fromImport
        .Where(i => items.ContainsKeyIgnoreCase(i.Key))
        .ForEach(i => { i.Value.ID = items.AtKey(i.Key).ID; });

    return newItems;

在执行的这一刻,没有什么可以访问数据本身,你可以 return 它,但不能交互。

如果您创建并测试此示例(断点位于 return 中的位置):

Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("a", 0);
dict.Add("b", 0);
dict.Add("c", 0);

dict.Add("A", 0);
dict.Add("B", 0);
dict.Add("C", 0);

var newItems = dict
    .Where(i => {
        return !dict.ContainsKeyIgnoreCase(i.Key);
    })
    .Select(i => i.Value);

Thread.Sleep(10000);

Console.WriteLine(newItems.Count());

你会看到在 10 秒标记之前不会命中断点 (在此屏幕截图中抢劫:https://imgur.com/a/uPaAOvS

至于你关于从未命中断点的评论,请确保你 return 对可枚举对象而不是副本的引用,并且如果你确实与它交互。

作为粗略的解决方案,您可以对代码进行以下更改:


    var newItems = fromImport
        .Where(i => !items.ContainsKeyIgnoreCase(i.Key))
        .Select(i => i.Value)
        .ToList();

这将强制执行您的查询,因为需要将数据复制到新列表中