Windows Metro异步加载数据

时间:2012-07-18 17:42:44

标签: c# microsoft-metro windows-runtime .net-4.5 async-await

我基于Split Page示例应用创建了一个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.

dataLoading.Wait()

只需锁定用户界面。

5 个答案:

答案 0 :(得分:5)

我认为如果你制作了构造函数async,这听起来是最好的选择。但是从that's not possible开始,您可以做的是为async创建MyDataSource工厂方法:

private MyDataSource()
{
    DataLoaded = false;
}

public static async Task<MyDataSource> Create()
{
    var source = new MyDataSource();
    await source.LoadData();
    return source;
}

然后使用_myDataSource初始化await

_myDataSource = await MyDataSource.Create();

如果由于某种原因,您无法执行此操作,则可以存储工厂方法返回的Task并在GetGroup()中等待它:

_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;
}

答案 1 :(得分:1)

如果LoadData是异步的,那么存储任何等待的东西(或创建一个新的)并公开它(例如,像任务一样)然后GetGroup可以被标记为异步并且可以做var data = await _myDataSource.LoadTask或者其他

答案 2 :(得分:1)

我认为Svick最接近回答问题是因为他使用了任务。无论是在GetGroup方法上返回任务还是在LoadAsync方法上返回任务,都可能无关紧要。重要的是你捕获该任务并在以后的简历中引用它。

任务类已记录here,您会注意到它具有 IsCanceled IsCompleted IsFaulted 等属性。当构造函数启动LoadAsync方法时,您可以将它返回的Task保存为类级变量。稍后,当您的简历代码启动时,您可以检查任务是否仍在运行(即未完成且未出现故障)。如果任务已完成,您可以立即运行简历代码。如果尚未完成,您可以使用 Task.ContinueWith 来安排在任务完成时运行简历代码。

答案 3 :(得分:0)

我还没有查看Windows 8,但我认为它与Windows Phone类似,假设您使用Silverlight(或WPF)来开发您的应用程序,我的问题并不清楚。如果您使用Silverlight:

您需要使用INotifyPropertyChanged接口和ObservableCollection-s才能将数据绑定到UI。这样,每当您更改数据时,UI都会收到有关更改的通知,并且绑定将刷新。

答案 4 :(得分:0)

MyDataGroup是一个实现iNotifyPropertyChanged的公共属性。

UniqueID是一个公共财产。 当UniqueID更改设置MyDataGroup = null然后调用BackGroundWorker到var mathces =但延迟,如果LoadData();工作中。由于这是在后台,延迟不会占用UI。在回调集中,MyDataGroup和UI将收到通知。确保后台工作者可取消,因为当更改UniqueID(如果它正在运行)时,您将要取消它。

我在WPF中这样做,如果它没有转换为Metro抱歉,我会删除。