C# windows8应用程序:嵌套异步调用
我正在构建一个Windows8应用程序,但在异步调用方面遇到了问题。我将尽力提供尽可能多的细节,因为我认为这有两个结果: 在异步调用方面,我做的事情是完全错误的 或者我做错了,但也可能是错误的架构让我遇到了这个问题,而这个问题本来就不应该存在。 我是WindowsAzure和MVVM的新手,但情况如下 该应用程序现在是为Windows8构建的,但我也希望能够使用其他平台,因此我首先要做的是创建一个WebAPI项目,该项目将发布到WindowsAzure网站。这样,我就可以使用JSON传输数据,WebAPI控制器连接到一个存储库,该存储库正在处理与WindowsAzure表存储之间的数据请求。第二部分是MVVM Light Windows 8应用程序,它从Azure网站请求数据 让我们更详细地了解一下WebAPI项目。这里我有一个分类模型C# windows8应用程序:嵌套异步调用,c#,windows-8,repository,mvvm-light,async-await,C#,Windows 8,Repository,Mvvm Light,Async Await,我正在构建一个Windows8应用程序,但在异步调用方面遇到了问题。我将尽力提供尽可能多的细节,因为我认为这有两个结果: 在异步调用方面,我做的事情是完全错误的 或者我做错了,但也可能是错误的架构让我遇到了这个问题,而这个问题本来就不应该存在。 我是WindowsAzure和MVVM的新手,但情况如下 该应用程序现在是为Windows8构建的,但我也希望能够使用其他平台,因此我首先要做的是创建一个WebAPI项目,该项目将发布到WindowsAzure网站。这样,我就可以使用JSON传输数据,W
public class Category : TableServiceEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
public string Parent { get; set; }
}
类别模型只包含名称和描述,id是
表服务实体。此外,如果类别嵌套,则会将字符串引用添加到父类别中。第一个问题出现了:父类是否应该是Category而不是string类型,后端的Category模型是否应该有子类的集合
然后我有我的IRepository接口来定义存储库。正在进行的工作它还使用规范模式传递查询范围。您可以使用浏览器进行测试并浏览到:
因此CategoryBasedViewModel的构造是获取类别并调用回调方法GetCategoriesCompleted:
_dataService.GetCategoriesAsync(GetCategoriesCompleted);
该回调方法还调用CategoryViewModel的构造函数。在那里,另一个异步方法用于获取类别的子级
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService
budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
_dataService.GetCategoriesByParentAsync(_category.RowKey,
GetCategoriesByParentCompleted);
}
这就是我的问题!GetCategoriesByParentAsync是发生在另一个异步调用内部的异步调用,代码只是从调用中中断出来,不执行任何操作
数据服务实现以下接口:
public interface IBudgetTrackerDataService
{
void GetCategoriesAsync(Action<IList<Category>, Exception> callback);
void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback);
}
异步方法包含以下代码:
public async void GetCategoriesAsync(Action<IList<Category>, Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories();
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
public async void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await
_client.GetCategoriesWithParent(parent);
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
长话短说:
为什么嵌套调用时这些调用失败?
第二,我是否愚蠢,我是否应该处理好父母/孩子
鸟笼之间的关系不同?
我现在要避开父/子关系问题,只解决异步问题 首先,关于异步代码,有两个通用准则,我在我的 避免异步void返回任务或任务。 如果适用,请使用ConfigureWaitFalse。 我见过其他人采用的回调委托方法,但我不确定它是从哪里来的。IMO说,它不能很好地与async一起工作,只会使代码复杂化。任务类型被设计为表示一个结果值和一个异常,它可以与wait无缝地工作 首先,您的数据服务:
public interface IBudgetTrackerDataService
{
Task<IList<Category>> GetCategoriesAsync();
Task<IList<Category>> GetCategoriesByParentAsync(string parent);
}
public async Task<IList<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories().ConfigureAwait(false);
return categoryEnumerable.ToList();
}
public async Task<IList<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetCategoriesWithParent(parent).ConfigureAwait(false);
return categoryEnumerable.ToList();
}
然后,您可以选择让父VM等待其子VM初始化。不清楚这是否是您想要的,但我假设您希望IsLoadingCategories为true,直到所有子VM都已加载:
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService)
{
_dataService = budgetTrackerDataService;
CategoryCollection = new ObservableCollection<CategoryViewModel>();
IsLoadingCategories = true;
Initialized = InitializeAsync();
NotifyOnInitializationErrorAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesAsync();
CategoryCollection.Clear();
foreach (var category in categories)
{
CategoryCollection.Add(new CategoryViewModel(category, _dataService));
}
// Wait until all CategoryViewModels have completed initializing.
await Task.WhenAll(CategoryCollection.Select(category => category.Initialized));
IsLoadingCategories = false;
}
private async Task NotifyOnInitializationErrorAsync()
{
try
{
await Initialized;
}
catch
{
NotifyPropertyChanged("InitializationError");
throw;
}
}
public string InitializationError { get { return Initialized.Exception.InnerException.Message; } }
我添加了InitializationError和NotifyOnInitializationErrorAsync,以演示一种显示初始化期间可能发生的任何错误的方法。由于Task未实现INotifyPropertyChanged,因此在初始化失败时没有自动通知,因此您必须显式地公开它。我现在要回避父/子关系问题,只解决异步问题 首先,关于异步代码,有两个通用准则,我在我的 避免异步void返回任务或任务。 如果适用,请使用ConfigureWaitFalse。 我见过其他人采用的回调委托方法,但我不确定它是从哪里来的。IMO说,它不能很好地与async一起工作,只会使代码复杂化。任务类型被设计为表示一个结果值和一个异常,它可以与wait无缝地工作 首先,您的数据服务:
public interface IBudgetTrackerDataService
{
Task<IList<Category>> GetCategoriesAsync();
Task<IList<Category>> GetCategoriesByParentAsync(string parent);
}
public async Task<IList<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories().ConfigureAwait(false);
return categoryEnumerable.ToList();
}
public async Task<IList<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetCategoriesWithParent(parent).ConfigureAwait(false);
return categoryEnumerable.ToList();
}
然后,您可以选择让父VM等待其子VM初始化。不清楚这是否是您想要的,但我假设您希望IsLoadingCategories为true,直到所有子VM都已加载:
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService)
{
_dataService = budgetTrackerDataService;
CategoryCollection = new ObservableCollection<CategoryViewModel>();
IsLoadingCategories = true;
Initialized = InitializeAsync();
NotifyOnInitializationErrorAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesAsync();
CategoryCollection.Clear();
foreach (var category in categories)
{
CategoryCollection.Add(new CategoryViewModel(category, _dataService));
}
// Wait until all CategoryViewModels have completed initializing.
await Task.WhenAll(CategoryCollection.Select(category => category.Initialized));
IsLoadingCategories = false;
}
private async Task NotifyOnInitializationErrorAsync()
{
try
{
await Initialized;
}
catch
{
NotifyPropertyChanged("InitializationError");
throw;
}
}
public string InitializationError { get { return Initialized.Exception.InnerException.Message; } }
我添加了InitializationError和NotifyOnInitializationErrorAsync,以演示一种显示初始化期间可能发生的任何错误的方法。由于任务未实现INotifyPropertyChanged,因此在初始化失败时没有自动通知,因此您必须显式地显示它。只是一个提示:如果您发布较短的问题,人们可能更愿意帮助您。如果你有不止一个问题,分别问他们。只是一个提示:人们可能更愿意帮助你
如果你发布简短的问题。如果你有不止一个问题,分别问他们。哇,斯蒂芬!这超出了我的期望:-巧合的是,我看到了斯科特·汉斯曼关于昨天的视频。但是你的博客帖子和你的回答告诉我如何在我的解决方案中实现它,这对我帮助很大!我将实施此功能,并让您知道它是否有效。谢谢!我正在尝试代码,但我有几个问题。首先,在CategoryViewModel的构造函数中,我看到:Initialized=InitializeAsync;但是财产只有一个get;所以是只读的,对吗?其次,这行代码:类别中的foreach var category不应该是:类别中的foreach var category.result??1该属性有一个私有setter,我在公共API描述中省略了它。但是,它是一个只读属性,仅在构造函数中设置。我忘记了等待;请参阅更新的代码。不要将Task.Result与异步代码一起使用;改用wait。我现在已经成功地让它工作了:-我这边还有一个问题:CategoryViewModel:initializeAsync方法不包含wait关键字,编译器警告它随后会同步执行?我将尝试查看父/子对象,看看我是否可以在那里做不同的事情。您应该等待对数据服务方法的调用。代码又更新了。哇,斯蒂芬!这超出了我的期望:-巧合的是,我看到了斯科特·汉斯曼关于昨天的视频。但是你的博客帖子和你的回答告诉我如何在我的解决方案中实现它,这对我帮助很大!我将实施此功能,并让您知道它是否有效。谢谢!我正在尝试代码,但我有几个问题。首先,在CategoryViewModel的构造函数中,我看到:Initialized=InitializeAsync;但是财产只有一个get;所以是只读的,对吗?其次,这行代码:类别中的foreach var category不应该是:类别中的foreach var category.result??1该属性有一个私有setter,我在公共API描述中省略了它。但是,它是一个只读属性,仅在构造函数中设置。我忘记了等待;请参阅更新的代码。不要将Task.Result与异步代码一起使用;改用wait。我现在已经成功地让它工作了:-我这边还有一个问题:CategoryViewModel:initializeAsync方法不包含wait关键字,编译器警告它随后会同步执行?我将尝试查看父/子对象,看看我是否可以在那里做不同的事情。您应该等待对数据服务方法的调用。代码再次更新。
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService
budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
_dataService.GetCategoriesByParentAsync(_category.RowKey,
GetCategoriesByParentCompleted);
}
public interface IBudgetTrackerDataService
{
void GetCategoriesAsync(Action<IList<Category>, Exception> callback);
void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback);
}
public async void GetCategoriesAsync(Action<IList<Category>, Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories();
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
public async void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await
_client.GetCategoriesWithParent(parent);
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
public interface IBudgetTrackerDataService
{
Task<IList<Category>> GetCategoriesAsync();
Task<IList<Category>> GetCategoriesByParentAsync(string parent);
}
public async Task<IList<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories().ConfigureAwait(false);
return categoryEnumerable.ToList();
}
public async Task<IList<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetCategoriesWithParent(parent).ConfigureAwait(false);
return categoryEnumerable.ToList();
}
public interface IBudgetTrackerDataService
{
Task<IEnumerable<Category>> GetCategoriesAsync();
Task<IEnumerable<Category>> GetCategoriesByParentAsync(string parent);
}
public Task<IEnumerable<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
return _client.GetAllCategories();
}
public Task<IEnumerable<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
return _client.GetCategoriesWithParent(parent);
}
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
Initialized = InitializeAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesByParentAsync(_category.RowKey);
...
}
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService)
{
_dataService = budgetTrackerDataService;
CategoryCollection = new ObservableCollection<CategoryViewModel>();
IsLoadingCategories = true;
Initialized = InitializeAsync();
NotifyOnInitializationErrorAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesAsync();
CategoryCollection.Clear();
foreach (var category in categories)
{
CategoryCollection.Add(new CategoryViewModel(category, _dataService));
}
// Wait until all CategoryViewModels have completed initializing.
await Task.WhenAll(CategoryCollection.Select(category => category.Initialized));
IsLoadingCategories = false;
}
private async Task NotifyOnInitializationErrorAsync()
{
try
{
await Initialized;
}
catch
{
NotifyPropertyChanged("InitializationError");
throw;
}
}
public string InitializationError { get { return Initialized.Exception.InnerException.Message; } }