C# Windows 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()是一种填充数据模型的异步方法。这工作正常,并在加载数据时显示数据(这是我想要的行为)。当我尝试测试挂起和终止时会

我已经基于拆分页面示例应用创建了一个Windows 8 Metro应用。但是,在示例应用程序中,数据是在构造函数中同步加载的。我正在访问一个文本文件,因此需要异步加载数据。构造函数如下所示:

    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方法上返回任务,都可能无关紧要。重要的是,您捕获了该任务,并在以后的简历中引用它


任务类是有文档记录的,您会注意到它的属性有IsCanceledIsCompletedIsFaulted。当构造函数启动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;
}