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
方法中的下一行代码是对数据库的查询,当数据库不复制时它就会失败。
任何人的任何见解都将不胜感激。
据我所知,这里的问题是您对 async
和 await
的使用并没有尽可能深入调用堆栈。
编写异步代码时,需要遵循一些通用规则 -
- 如果函数
Foo
使用 async
关键字,则调用 Foo
的任何其他函数也必须使用 async
关键字。
- 切勿在函数签名中使用
async void
- 请改用 async Task
。例外情况是:
- 您的函数是事件处理程序(例如按下按钮)
- 或者您正在重写恰好 [=74=]
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 停止,因此使用新方法是有利的。
我希望有人能阐明我遇到的问题。我正在编写一个 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
方法中的下一行代码是对数据库的查询,当数据库不复制时它就会失败。
任何人的任何见解都将不胜感激。
据我所知,这里的问题是您对 async
和 await
的使用并没有尽可能深入调用堆栈。
编写异步代码时,需要遵循一些通用规则 -
- 如果函数
Foo
使用async
关键字,则调用Foo
的任何其他函数也必须使用async
关键字。 - 切勿在函数签名中使用
async void
- 请改用async Task
。例外情况是:- 您的函数是事件处理程序(例如按下按钮)
- 或者您正在重写恰好 [=74=]
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 停止,因此使用新方法是有利的。