System.IO.Directory.Exists 在 LINQ 语句中失败,但在 foreach 循环中不失败

System.IO.Directory.Exists fails inside LINQ statement, but not in a foreach loop

.Net Framework 4.7.2

这是一个非常令人惊讶的问题...

我有这个例程从本地磁盘获取所有可用的 Inventor 模板,无论它是哪个版本:

private static IEnumerable<string> GetInventorTemplates_FAILS()
{
    var publicPath = Environment.GetEnvironmentVariable("PUBLIC");
    var autodeskPath = Path.Combine(publicPath, "Documents", "Autodesk");
    var inventorPaths = Directory.GetDirectories(autodeskPath, "*Inventor*");
    var templatePaths = inventorPaths.Select(path => Path.Combine(path, "Templates"));
    var templates = templatePaths.Where(Directory.Exists).SelectMany(path => Directory.GetFiles(path, "*.*", SearchOption.AllDirectories));
    //                     throws error ^^^^^^^^^^^^^^^^

    return templates;
}

如果我运行这个代码,我得到这个错误:

System.Linq.SystemCore_EnumerableDebugView`1[System.String].get_Items() calls into native method Microsoft.Win32.Win32Native.GetFullPathName(char*, int, char*, System.IntPtr). Evaluation of native methods in this context is not supported.  

更疯狂的是,如果我重写代码,手动调用 Directory.Exists 就可以了!!!

private static IEnumerable<string> GetInventorTemplates_WORKS()
{
    var publicPath = Environment.GetEnvironmentVariable("PUBLIC");
    var autodeskPath = Path.Combine(publicPath, "Documents", "Autodesk");
    var inventorPaths = Directory.GetDirectories(autodeskPath, "*Inventor*");
    var templatePaths = inventorPaths.Select(path => Path.Combine(path, "Templates"));

    foreach (var path in templatePaths)
        if (Directory.Exists(path)) // <- no exception!!!
            foreach (var template in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories))
                yield return template;
}

如果您通过 QuickWatch:

检查,您会得到相同的结果

现在我可以使用重写的版本,但我很好奇我是不是幸运儿...:-)

这不是 .NET 问题。该代码 运行 没问题,您无需更改任何内容。 调试器 表示它无法在手表 window 中安全地执行查询,因此它无法显示任何结果。错误说明

Evaluation of native methods in this context is not supported.

template 不是文档列表,它是尚未执行的查询。调试器必须执行该查询才能检索结果。

这既不是问题也不罕见。在多种情况下,调试器无法安全地执行 LINQ 查询或枚举 IEnumerable 并显示结果。如果要检查结果,请显式执行查询,例如 ToList.

PS

使用像 Glob(3M NuGet 下载)这样的 globbing 库来替换所有这些代码可能是个好主意:

var root = new DirectoryInfo(autodeskPath);
var templates = root.GlobFileSystemInfos("*Inventor*/Templates/**/*.*");

.NET Core本身使用了file globbing through the Microsoft.Extensions.FileSystemGlobbing包。

Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*Inventor*/Templates/**/*.*" });

foreach (string file in matcher.GetResultsInFullPath(autodeskPath))
{
    Console.WriteLine(file);
}