【问题标题】:Async and LINQ Queries: Retrieve all lines in all files in a directory tree异步和 LINQ 查询:检索目录树中所有文件中的所有行
【发布时间】:2018-11-12 10:30:17
【问题描述】:

我有一些我正在尝试维护的 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

然而,自 10 月下旬以来,关于文件权限 (Source) 的一些事情似乎发生了变化。经过大约一个月的工作后,我回到了代码,发现它抛出了一个异常,说我以前能够写入的文件夹由于权限而无法访问。该文件夹存在,我从选择器中抓取了该文件夹,所以我认为我很好。以前,只要我使用选择器抓取了文件或文件夹,我仍然可以使用 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 中的第二个等待,我至少能够枚举文件夹。问题是使用 LINQ 查询中的第二个 Await,我无法编译为

'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 函数的使用,但我无法确切地了解它是如何付诸实践的。

How to await a method in a Linq query

所以我尝试了:

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)(),奇怪的是它对我不起作用。也许这只是一个严重的误解。

无论如何,对理解异步文件操作的任何帮助都非常有用!

【问题讨论】:

  • 请查看此案例reply
  • @CoCalceDew 我看过那个帖子,它包含在我原来的问题中。我需要澄清一下应该如何使用它。任何帮助都会得到帮助。

标签: vb.net linq uwp async-await


【解决方案1】:

正如斯蒂芬在thread 中所说,LINQ 对async/await 的支持非常有限。我建议您将For EachWindows.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

【讨论】:

  • 感谢您的回答。有趣的是,这几乎与我最终生成的当前工作代码相同。我真的很喜欢能够生成一个非常强大的、单一的代码行,它将文件枚举到我指定的深度并同时检索不同的行。当然,我可以写一个扩展,但如果这不是太夸张的话,这似乎有点失去表达力。尽管如此,我的测试中的枚举完成得更快,并不是说应用程序对它更好;我需要枚举的文件会生成一个总大小 的 xml
猜你喜欢
  • 1970-01-01
  • 2016-08-23
  • 1970-01-01
  • 2011-04-11
  • 1970-01-01
  • 2014-06-28
  • 2012-09-16
  • 2014-04-26
  • 2010-11-22
相关资源
最近更新 更多