C# 在EnumerateFiles()LINQ C中处理(跳过)UnauthorizedAccessException

C# 在EnumerateFiles()LINQ C中处理(跳过)UnauthorizedAccessException,c#,performance,linq,search,C#,Performance,Linq,Search,我正在尝试编写一个高性能的文件系统搜索程序,它可以搜索未索引的驱动器,包括本地驱动器和网络驱动器,并对扩展名和关键字进行快速过滤。我试图使用C的DirectoryInfo.EnumerateDirectory、DirectoryInfo.EnumerateFiles和LINQ查询来实现这一点。根据我的测试,这是迄今为止我能找到的性能最好的代码: FileInfo[] dirFiles = dirInfo.EnumerateDirectories()

我正在尝试编写一个高性能的文件系统搜索程序,它可以搜索未索引的驱动器,包括本地驱动器和网络驱动器,并对扩展名和关键字进行快速过滤。我试图使用C的DirectoryInfo.EnumerateDirectory、DirectoryInfo.EnumerateFiles和LINQ查询来实现这一点。根据我的测试,这是迄今为止我能找到的性能最好的代码:


FileInfo[] dirFiles = dirInfo.EnumerateDirectories()
                            .AsParallel()
                            .SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories)
                                                .Where(fi => EndsWithExtension(fi.Extension)) )
                                                .ToArray();
但是,UnauthorizedAccessException不会被处理,抛出时会使整个查询崩溃

我已经尝试过与此问题相关的各种方法,但我发现它们在搜索性能方面明显较慢。我发现第二好的方法工作起来要慢20多倍,例如:

try {
    foreach (string fileName in EnumerateFiles(dirInfo, "*.*", SearchOption.AllDirectories)) {
        if (ContainsKeyword(fileName)) {
             Results.Add(fileName.FullName);
        }
    }
} catch (Exception e) { continue; }

当目录抛出异常时,我想跳过它。我一直在尝试用类似的东西来实现这一点,但我无法让它发挥作用我对LINQ和枚举的知识太有限了…:

FileInfo[] dirFiles = dirInfo.EnumerateDirectories()
                            .AsParallel()
                            .SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories)
                                                .SkipExceptions()
                                                .Where(fi => EndsWithExtension(fi.Extension)) )
                                                .ToArray();

public static class Extensions {
        public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values) {
            using (var enumerator = values.GetEnumerator()) {
                bool next = true;
                while (next) {
                    try {
                        if (enumerator.Current != null)
                            Console.WriteLine(enumerator.Current.ToString()); 
                        next = enumerator.MoveNext();
                    } catch {
                        continue;
                    }

                    if (next) yield return enumerator.Current;
                }
            }
        }
    }
是否可以处理未经授权的数据访问异常,同时仍保持原始LINQ查询的高性能

提前感谢您的帮助

答案已编辑: 解决方法是递归调用它,而不是使用SearchOption.AllDirectories。在您的情况下,这实际上效率更低,因为您不需要将文件系统中的每个文件加载到一个数组中。从以下帮助器方法开始:

    List<string> GetDirectoriesRecursive (string parent)
    {
        var directories = new List<string>();
        GetDirectoriesRecursive (directories, parent);
        return directories;
    }

    void GetDirectoriesRecursive (List<string> directories, string parent)
    {
        directories.Add (parent);
        foreach (string child in GetAuthorizedDirectories (parent))
            GetDirectoriesRecursive (directories, child);
    }

    string[] GetAuthorizedDirectories (string dir)
    {
        try { return Directory.GetDirectories (dir); }
        catch (UnauthorizedAccessException) { return new string[0]; }
    }

    string[] GetAuthorizedFiles (string dir)
    {
        try { return Directory.GetFiles (dir); }
        catch (UnauthorizedAccessException) { return new string[0]; }
    }
或者,仅获取其目录:

     var foldersWithBigFiles =
         from dir in GetDirectoriesRecursive ( @"c:\" )
         where GetAuthorizedFiles (dir).Any (f => new FileInfo (f).Length > 100000000 )
         select dir;
另一种方法:

 string[] directories = Directory.EnumerateDirectories(@"\\testnetwork\abc$","*.*", SearchOption.AllDirectories).Catch(typeof(UnauthorizedAccessException)).ToArray();
新增缺失部分:

static class ExceptionExtensions
{
public static IEnumerable<TIn> Catch<TIn>(
            this IEnumerable<TIn> source,
            Type exceptionType)
{   
    using (var e = source.GetEnumerator())
    while (true)
    {
        var ok = false;

        try
        {
            ok = e.MoveNext();
        }
        catch(Exception ex)
        {
            if (ex.GetType() != exceptionType)
                throw;
            continue;
        }

        if (!ok)
            yield break;

        yield return e.Current;
    }
}
}


对于链接是否包含解决方案,这是一个描述不当的答案。你应该在回答中详细说明至少部分链接。在你编辑后更改了我的投票。谢谢你的回答。愉快的编码。谢谢你的回答,但这并不是我问题的解决方案。使用各种helper方法的第一个建议有很多开销,它会将filepath字符串转换为FileInfo对象,通过递归建立大量内存。这会大大降低查询速度。后者确实更优雅。我已经看到了这个解决方案,但是你忘记了在最重要的部分复制:ExceptionExtension,但是我无法让它工作。它仍然抛出UnauthorizedAccessException。
static class ExceptionExtensions
{
public static IEnumerable<TIn> Catch<TIn>(
            this IEnumerable<TIn> source,
            Type exceptionType)
{   
    using (var e = source.GetEnumerator())
    while (true)
    {
        var ok = false;

        try
        {
            ok = e.MoveNext();
        }
        catch(Exception ex)
        {
            if (ex.GetType() != exceptionType)
                throw;
            continue;
        }

        if (!ok)
            yield break;

        yield return e.Current;
    }
}
}