Асинхронные и LINQ-запросы: получение всех строк во всех файлах в дереве каталогов - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть некоторый код UWP, который я пытаюсь сохранить. Его цель - объединить некоторые текстовые файлы, хранящиеся в структуре каталогов, в один файл XML.

Чтобы сделать это, я предварительно собрал все отдельные строки во всех файлах в структуре директории, используя LINQ:

Imports System.IO

Async Function getAllLines() As IEnumerable(Of String)
    Dim _folderPicker As New Windows.Storage.Pickers.FolderPicker()
    _folderPicker.FileTypeFilter.Add("*") 'We must add this; it is a known bug
    Dim _rootDir As Windows.Storage.StorageFolder = Await _folderPicker.PickSingleFolderAsync()

    Dim allDistinctLines As IEnumerable(Of String) = From _file In Directory.GetFiles(_rootDir.Name, "*.txt", SearchOption.AllDirectories) From line In File.ReadAllLines(_file) Where line.Trim().Length > 0 Select line.Trim() Distinct

    Return allDistinctLines
End Function

Однако с конца октября, похоже, что-то изменилось в отношении прав доступа к файлам (Источник) . Я вернулся к коду примерно через месяц после того, как он работал, и обнаружил исключение, в котором говорилось, что папка, в которую я ранее мог записывать, была недоступна из-за разрешений. Папка существовала, и я взял папку у сборщика, поэтому подумал, что у меня все хорошо. Ранее, пока я получал файл или папку с помощью средства выбора, я все еще мог использовать функцию System.IO для перечисления, чтения или записи в указанную папку или каталог.

Теперь кажется, что вы должны использовать правильные способности UWP, что хорошо, но я не уверен, как переписать мой запрос LINQ, поскольку методы UWP асинхронны.

Вот что я пробовал:

Async Function getAllLines() As IEnumerable(Of String)
    Dim _folderPicker As New Windows.Storage.Pickers.FolderPicker()
    _folderPicker.FileTypeFilter.Add("*") 'We must add this; it is a known bug
    Dim _rootDir As Windows.Storage.StorageFolder = Await _folderPicker.PickSingleFolderAsync()

    Dim queryOptions As New Windows.Storage.Search.QueryOptions()
    queryOptions.FileTypeFilter.Add(".txt")
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep

    Dim allDistinctLines As IEnumerable(Of String) = From _file In Await _rootDir.CreateFileQueryWithOptions(queryOptions).GetFilesAsync() From line In Await Windows.Storage.FileIO.ReadLinesAsync(_file) Where line.Trim().Length > 0 Select line.Trim() Distinct

    Return allDistinctLines
End Function

В приведенном выше коде я по крайней мере могу перечислить папку, если закомментирую второе ожидание в LINQ. Проблема в том, что со вторым Await в запросе LINQ я не могу скомпилировать как

'Await' may only be used in a query expression within the first collection expression of the initial 'From' clause or within the collection expression of a 'Join' clause.

Итак, я прочитал предложения Join, которые, похоже, приравнивают разрозненные данные, что на самом деле не то, что я ищу.

Я прочитал об ошибке в целом, и кажется, что запросы LINQ и асинхронные функции имеют только ограниченную функциональность. Честно говоря, я не знаю достаточно о LINQ, чтобы действительно заявить об этом, но это было мое чувство.

Я нашел этот ответ, который использует Task.WhenAll () для облегчения использования функций LINQ и Async, но я не могу точно понять, как это будет осуществляться на практике.

Как ждать метод в запросе Linq

Итак, я попробовал:

Async Function getAllLines() As IEnumerable(Of String)
    Dim _folderPicker As New Windows.Storage.Pickers.FolderPicker()
    _folderPicker.FileTypeFilter.Add("*") 'We must add this; it is a known bug
    Dim _rootDir As Windows.Storage.StorageFolder = Await _folderPicker.PickSingleFolderAsync()

    Dim queryOptions As New Windows.Storage.Search.QueryOptions()
    queryOptions.FileTypeFilter.Add(".txt")
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep

    Dim allFiles As IEnumerable(Of Windows.Storage.StorageFile) = From _file In Await _rootDir.CreateFileQueryWithOptions(queryOptions).GetFilesAsync() Select _file

    Dim allDistinctLines As IEnumerable(Of String) = Task.WhenAll(allFiles.Select(Function(_file) Windows.Storage.FileIO.ReadLines.Async(_file).Where(Function(_line) _line.Trim().Length > 0).Distinct()))

    Return allDistinctLines
End Function

Но тип возвращаемого значения не тот, он заканчивается каким-то странным IList (Of String) (), что странным образом не работает для меня. Может быть, там есть просто критическое недоразумение.

В любом случае, любая помощь в понимании операций с асинхронными файлами ценится!

1 Ответ

0 голосов
/ 12 ноября 2018

Как сказал Стивен в этой теме , LINQ имеет очень ограниченную поддержку async/await. Я рекомендую использовать For Each в сочетании с Windows.Storage api, как показано ниже:

Async Function getAllLines() As Task(Of IEnumerable(Of String))
    Dim _folderPicker As New Windows.Storage.Pickers.FolderPicker()
    _folderPicker.FileTypeFilter.Add("*") 'We must add this; it is a known bug
    Dim _rootDir As Windows.Storage.StorageFolder = Await _folderPicker.PickSingleFolderAsync()

    Dim queryOptions As New Windows.Storage.Search.QueryOptions()
    queryOptions.FileTypeFilter.Add(".txt")
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep

    Dim allDistinctLines As New List(Of String)

    For Each file As StorageFile In Await _rootDir.CreateFileQueryWithOptions(queryOptions).GetFilesAsync()
        For Each line In Await Windows.Storage.FileIO.ReadLinesAsync(file)
            If line.Trim().Length > 0 Then
                allDistinctLines.Add(line.Trim())
            End If
        Next
    Next

    Return allDistinctLines.Distinct()
End Function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...