Windows 运行 Time GetFileFromPathAsync 未返回

Windows Run Time GetFileFromPathAsync Not Returning

我希望有人能阐明我遇到的问题。我正在编写一个 Windows Store 应用程序,它在启动时需要将 "seed" 数据库从应用程序安装文件夹 Windows.ApplicationModel.Package.Current.InstalledLocation.Path 复制到本地数据文件夹 Windows.Storage.ApplicationData.Current.LocalFolder。为此,我使用了从 Main() 中的 OnNavigatedTo 方法调用的以下内容。

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    //Calls  method in Dal Class which ensures database is in correct location and if not copies it
    Dal.LoadData();  

    RefreshScenarioList();
    ScenarioList.SelectedIndex = 0;  //this is the Default Item             
}

public static async void LoadData()
{
    await CopyIfNotExists(dbName);
}

private static async Task CopyIfNotExists(string dbName)
{
    if (await GetIfFileExistsAsync(dbName) == null)
    {

        StorageFile seedFile = await StorageFile.GetFileFromPathAsync(
        Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path,
        dbName));

        await seedFile.CopyAsync(Windows.Storage.ApplicationData.Current.LocalFolder);
    }
}

private static async Task<StorageFile> GetIfFileExistsAsync(string key)
{
    try
    {
        return await ApplicationData.Current.LocalFolder.GetFileAsync(key);

    }
    catch (FileNotFoundException) { return default(StorageFile); }
}

另请注意,我也使用了 GetFileAsync 并尝试使用 uri 来获取种子文件(以查看是否有任何差异)。通过这里的组合,我似乎获得了最高的成功率。这似乎在每 4 次尝试中工作 2-3 次,但我会更愿意接受 100% 的时间工作的东西。启动时只需要复制一次数据库。

当它不起作用时,它 "seems" 永远不会 return(即 CopyIfNotExists 似乎永远不会完成)。问题似乎出在 CopyIfNotExists 方法中。问题是实际上我的 OnNavigatedTo 方法中的下一行代码是对数据库的查询,当数据库不复制时它就会失败。

任何人的任何见解都将不胜感激。

据我所知,这里的问题是您对 asyncawait 的使用并没有尽可能深入调用堆栈。

编写异步代码时,需要遵循一些通用规则 -

  1. 如果函数 Foo 使用 async 关键字,则调用 Foo 的任何其他函数也必须使用 async 关键字。
  2. 切勿在函数签名中使用 async void - 请改用 async Task。例外情况是:
    • 您的函数是事件处理程序(例如按下按钮)
    • 或者您正在重写恰好 [=7​​4=] void
    • 的方法

让我们看一下调用堆栈上的方法签名。 OnNavigatedTo 显示在顶部:

      override void              OnNavigatedTo()
async          void              LoadData()
async          Task              CopyIfNotExists()
async          Task<StorageFile> GetIfFileExistsAsync()

调用堆栈底部的两个方法在方法签名中使用 async Task。在调用堆栈的上方是 LoadData,它遵循上面的规则 1,但违反了规则 2。LoadData 应更改为:

public static async Task LoadData()
{
    await CopyIfNotExists(dbName);
}

接下来我们尝试让OnNavigatedTo遵循上面的规则:

async protected override void OnNavigatedTo(NavigationEventArgs e)

请注意,我们不能使用 async Task,因为您的方法是覆盖。我们必须坚持 async void.

现在您只需将 await 添加到您的 LoadData 调用中,因为它是一个 async 方法。我们这样做是为了让您的程序在调用 RefreshScenarioList:

之前等待 LoadData 完成
async protected override void OnNavigatedTo(NavigationEventArgs e)
{
    //Calls  method in Dal Class which ensures database is in correct location and if not copies it
    await Dal.LoadData();  

    RefreshScenarioList();
    ScenarioList.SelectedIndex = 0;  //this is the Default Item             
}

现在您还需要确保 RefreshScenarioList 的调用堆栈遵循上述异步规则。


我在您的代码中还注意到一件事。在 Windows 8.1 或更高版本中,您应该使用 TryGetItemAsync 而不是 GetFileAsync:

private static async Task<StorageFile> GetIfFileExistsAsync(string key)
{
    return await ApplicationData.Current.LocalFolder.TryGetItemAsync(key) as StorageFile;
}

找不到文件时的新方法returns null。旧方法抛出异常。在 UI 线程上抛出异常会导致您的 UI 停止,因此使用新方法是有利的。