Перечисление локальных файлов LINQ с использованием DriveInfo

#c# #linq #driveinfo

#c# #linq #driveinfo

Вопрос:

Я знаю, что есть много способов выполнить то, что я пытаюсь сделать здесь, но я хотел попробовать это с LINQ, и я вижу некоторые странные (для меня) результаты. Надеюсь, у кого-нибудь здесь есть какая-то мудрость, чтобы предложить мне.

Цель состоит в том, чтобы найти диск на локальной машине, на котором мы выполняем, с максимально доступным свободным пространством, отформатированным в файловой системе NTFS, исключая том %SystemDrive%, если это возможно. Еще одно предостережение заключается в том, что диски, которым присвоена буква тома, но которые еще не были отформатированы, должны быть исключены из пула кандидатов. Это крайний случай, но один клиент уже сталкивался с ним.

Итак, я пытаюсь:

 string systemDrive = Environment.GetEnvironmentVariable("SystemDrive")   "\";

List<DriveInfo> localDrives = DriveInfo.GetDrives().ToList();

var bestAPIDataDrives = from localDrive in localDrives
            orderby localDrive.AvailableFreeSpace descending
            where   localDrive.DriveType == DriveType.Fixed amp;amp;
                    localDrive.DriveFormat == "NTFS" amp;amp;
                    localDrive.RootDirectory.FullName != systemDrive amp;amp;
                    localDrive.IsReady
            select  localDrive.RootDirectory.FullName;

string path = String.Empty;

if (bestAPIDataDrives.Count() == 0)
{
    // there is only a system drive available, use it anyway.
    path = systemDrive;
}
else
{
    path = bestAPIDataDrives.ElementAt(0).ToString();
}
  

Когда я доберусь до bestAPIDataDrives.По ссылке Count() я получаю исключение IOException со свойством Message, для которого установлено значение «Устройство не готово».

Я не понимаю, почему это происходит, потому что у меня localDrive.Готов ли тест в запросе LINQ, или, по крайней мере, я думаю, что готов.

Комментарии:

1. Марк ответил на реальный вопрос, поэтому два комментария к обзору кода: Этот код фактически выполняет запрос дважды, один раз для подсчета и еще раз для поиска первого элемента. Вам также не обязательно вводить строку. Все, что после построения запроса, может быть заменено на string path = bestAPIDataDrives.FirstOrDefault() ?? systemDrive . Вам также не нужно создавать список из результатов DriveInfo.GetDrives() , и вы можете сказать IEnumerable<DriveInfo> localDrives = DriveInfo.GetDrives() .

2. @Chris — хорошее место; Я определенно согласен — следует использовать FirstOrDefault() здесь (и ничего больше; это охватывает оба Count() и ElementAt() , гораздо эффективнее)

3. Еще раз хороший совет для всех. Большое спасибо. Обновит решение с помощью этих элементов проверки кода.

Ответ №1:

это LINQ-to-Objects, поэтому он будет выполнять операции в представленном порядке; в частности, он попытается выполнить сортировку перед фильтрацией, и почти все фильтры выполняются перед тестированием ключа IsReady . Я бы просто изменил их порядок:

 var bestAPIDataDrives = from localDrive in localDrives
            where   localDrive.IsReady amp;amp;
                    localDrive.DriveType == DriveType.Fixed amp;amp;
                    localDrive.DriveFormat == "NTFS" amp;amp;
                    localDrive.RootDirectory.FullName != systemDrive     
            orderby localDrive.AvailableFreeSpace descending                   
            select  localDrive.RootDirectory.FullName;
  

Выполнение сортировки после фильтров в любом случае является хорошей идеей, поскольку вы снижаете стоимость сортировки (которая всегда является суперлинейной)

Как отмечает Крис, затем вы просто хотите закончить с:

 string path = bestAPIDataDrives.FirstOrDefault() ?? systemDrive;