C# Windows Metro异步加载数据
我已经基于拆分页面示例应用创建了一个Windows 8 Metro应用。但是,在示例应用程序中,数据是在构造函数中同步加载的。我正在访问一个文本文件,因此需要异步加载数据。构造函数如下所示:C# Windows Metro异步加载数据,c#,microsoft-metro,windows-runtime,.net-4.5,async-await,C#,Microsoft Metro,Windows Runtime,.net 4.5,Async Await,我已经基于拆分页面示例应用创建了一个Windows 8 Metro应用。但是,在示例应用程序中,数据是在构造函数中同步加载的。我正在访问一个文本文件,因此需要异步加载数据。构造函数如下所示: public MyDataSource() { DataLoaded = false; LoadData(); } LoadData()是一种填充数据模型的异步方法。这工作正常,并在加载数据时显示数据(这是我想要的行为)。当我尝试测试挂起和终止时会
public MyDataSource()
{
DataLoaded = false;
LoadData();
}
LoadData()
是一种填充数据模型的异步方法。这工作正常,并在加载数据时显示数据(这是我想要的行为)。当我尝试测试挂起和终止时会出现问题。问题在于,恢复可能会在填充数据模型之前尝试访问该数据模型:
public static MyDataGroup GetGroup(string uniqueId)
{
// If the data hasn't been loaded yet then what?
if (_myDataSource == null)
{
// Where app has been suspended and terminated there is no data available yet
}
// Simple linear search is acceptable for small data sets
var matches = _myDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
return null;
}
我可以通过将构造函数更改为调用LoadData().Wait
,来解决这个问题,但这意味着应用程序会锁定UI线程。我认为我需要的是一种在GetGroup
中获取恢复代码的方法,以便在不锁定UI线程的情况下等待数据加载。这是可能的还是可取的,如果是,如何做到
编辑:
一两个人建议将任务缓存到LoadData()
。这是一个很好的想法,但是GetGroup
中的代码由页面状态管理部分调用,因此不能是异步的。为了解决这个问题,我尝试了以下方法:
if (!DataLoaded)
{
//dataLoading = await MyDataSource.LoadData();
dataLoading.RunSynchronously();
}
但这给了我一个错误:
RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.
及
只是锁定UI。我还没有签出Windows 8,但我假设它的工作原理与Windows Phone类似,假设您使用Silverlight(或WPF)开发应用程序,从您的问题中我不清楚这一点。如果您使用Silverlight:
您需要使用INotifyPropertyChanged接口和ObservableCollection-s将数据绑定到UI。这样,每次更改数据时,用户界面都会收到有关更改的通知,绑定也会刷新。如果LoadData是异步的,则存储等待的内容(或创建新的内容)并公开(例如,像任务一样)然后可以将GetGroup标记为async,并可以执行var data=await\u myDataSource.LoadTask或任何类似的操作。我认为,如果将构造函数设置为async,那么这似乎是最好的选择。但是因为,您可以做的是为
MyDataSource
创建一个async
工厂方法:
private MyDataSource()
{
DataLoaded = false;
}
public static async Task<MyDataSource> Create()
{
var source = new MyDataSource();
await source.LoadData();
return source;
}
如果由于某种原因无法执行此操作,则可以存储工厂方法返回的任务
,并在GetGroup()
中等待它:
\u myDataSourceTask=MyDataSource.Create();
…
公共静态异步任务GetGroup(字符串唯一ID)
{
var myDataSource=wait_myDataSourceTask;
//对于小数据集,简单线性搜索是可以接受的
var matches=myDataSource.AllGroups.Where(group=>group.UniqueId==UniqueId);
if(matches.Count()==1)返回matches.First();
返回null;
}
MyDataGroup实现iNotifyPropertyChanged的公共属性
唯一的公共财产。
当UniqueID更改时,设置MyDataGroup=null,然后调用BackGroundWorker到var mathces=但如果LoadData()则延迟;他正在工作。由于这是在后台,延迟不会占用UI。在回调集合中,MyDataGroup和UI将收到通知。确保backgroundworker是可取消的,因为当UniqueID更改时(如果它正在运行),您将希望取消它
我在WPF中这样做,所以如果它没有翻译成Metro对不起,我会删除 我认为斯维克最接近于回答这个问题,因为他使用任务。无论是在GetGroup方法上返回任务,还是在LoadAsync方法上返回任务,都可能无关紧要。重要的是,您捕获了该任务,并在以后的简历中引用它
任务类是有文档记录的,您会注意到它的属性有IsCanceled、IsCompleted和IsFaulted。当构造函数启动LoadAsync方法时,可以将它返回的任务保存为类级变量。稍后,当您的简历代码启动时,您可以检查任务是否仍在运行(即未完成和未出现故障)。如果任务已完成,您可以立即运行简历代码。如果任务尚未完成,您可以使用Task.ContinueWith计划在任务完成时运行您的简历代码。为什么不在另一个线程中启动LoadData()?我不知道
LoadData()
是否是自定义方法,如果是您编写的代码,我高度怀疑这就是问题所在;IsLoading=true;异步加载完成时,设置IsLoading=false;在GetGroup中,检查IsLoading==true;如果是这样的话,等等。@bum-那是我的想法。但是如何在不锁定UI线程的情况下等待?@pm_2:如果你可以获得/存储一个waitiable,那么你可以让waiting为你做这件事。它让GetGroup拥有可以工作的代码,而不管加载是否已完成,但如果找到多个匹配项,是否确实要返回null
?我认为异常更有意义,在这种情况下,您可以使用SingleOrDefault()
简化代码。绑定工作正常。如问题中所述,此问题与挂起和终止后的事件顺序有关。不幸的是,这在实践中不起作用。原因是恢复应用程序状态的函数(LoadState
)调用了GetGroup
,因此无法使其异步(GetGroup不能异步)。您仍然可以从非异步方法启动异步操作。您不会等到它完成(除非您想阻止)。请参见上面的编辑-显然,在本例中,如果我不等待它完成,那么我将以与我在中开始时相同的情况结束。如果您不能异步等待,也不想同步等待,那么这意味着您将以未完成的任务
结束。我对温特了解不够,无法进一步帮助你
private MyDataSource()
{
DataLoaded = false;
}
public static async Task<MyDataSource> Create()
{
var source = new MyDataSource();
await source.LoadData();
return source;
}
_myDataSource = await MyDataSource.Create();
_myDataSourceTask = MyDataSource.Create();
…
public static async Task<MyDataGroup> GetGroup(string uniqueId)
{
var myDataSource = await _myDataSourceTask;
// Simple linear search is acceptable for small data sets
var matches = myDataSource.AllGroups.Where(group => group.UniqueId == uniqueId);
if (matches.Count() == 1) return matches.First();
return null;
}