MVVM 和异步数据访问

本文关键字:访问 数据 异步 MVVM | 更新日期: 2023-09-27 18:30:25

所以我有一个使用MVVM模式(Caliburn.Micro)的WPF应用程序。我把视图和视图模型连接起来,基本上缺少的是数据。数据将从 WCF 服务、本地存储或内存/缓存中"按需"检索 - 原因是允许脱机模式并避免不必要的服务器通信。另一个要求是异步检索数据,以便不会阻止 UI 线程。

所以我在考虑创建某种视图模型用来请求数据的"资源管理器":

_someAssetManager.GetSomeSpecificAsset(assetId, OnGetSomeSpecificAssetCompleted)

请注意,这是一个异步调用。不过,我遇到了几个不同的问题。如果不同的视图模型(大致)同时请求相同的资产,我们如何确保我们不会做不必要的工作,并且它们都获得我们可以绑定的相同对象?

不确定我是否有正确的方法。我一直在看反应式框架 - 但我不知道如何在这种情况下使用它。关于我可以使用的框架/技术/模式的任何建议?这似乎是一个相当普遍的情况。

MVVM 和异步数据访问

Dictionary<int, IObservable<IAsset>> inflightRequests;
public IObservable<IAsset> GetSomeAsset(int id)
{
    // People who ask for an inflight request just get the
    // existing one
    lock(inflightRequests) {
        if inflightRequests.ContainsKey(id) {
            return inflightRequests[id];
        }
    }
    // Create a new IObservable and put in the dictionary
    lock(inflightRequests) { inflightRequests[id] = ret; }
    // Actually do the request and "play it" onto the Subject. 
    var ret = new AsyncSubject<IAsset>();
    GetSomeAssetForReals(id, result => {
        ret.OnNext(id);
        ret.OnCompleted();
        // We're not inflight anymore, remove the item
        lock(inflightRequests) { inflightRequests.Remove(id); }
    })
    return ret;
}

我在方法调用方面取得了成功,这些调用传入在收到数据时调用的委托。 您可以通过检查确定请求是否正在发生的布尔字段来分层要求,使每个人都使用相同的数据(如果当前正在发生请求)。 我会保留一个需要调用的委托的本地集合,以便在最终收到数据时,包含要调用的委托的类可以迭代它们,传入新接收的数据。

大致如下:

public interface IViewModelDataLoader{
    void LoadData(AssignData callback);
}
public delegate void AssignData(IEnumerable<DataObject> results);

然后,实际实现此接口的类可以对数据完成时通知谁进行运行统计(假设是单例模型):

public class ViewModelDataLoader : IViewModelDataLoader{
    private IList<AssignData> callbacksToCall;
    private bool isLoading;
    public void LoadData(AssignData callback){
        callbacksToCall.add(callback);
        if (isLoading) { return; }
        // Do some long running code here
        var data = something;
        // Now iterate the list
        foreach(var item in callbacksToCall){
           item(data);
        }
        isLoading = false;
    }
 }

使用代理模式和事件,您可以同时提供同步和异步数据。让您的代理返回同步调用的缓存值,并在接收异步数据时通过事件通知视图模型。代理还可以设计为跟踪数据请求和限制服务器连接(例如"引用计数"调用、请求的数据/接收数据标志等)

我会像这样设置你AssetManager

public interface IAssetManager
{
    IObservable<IAsset> GetSomeSpecificAsset(int assetId);
}

在内部,您需要返回异步填充的Subject<IAsset>。做对了,每次调用只有一个呼叫GetSomeSpecificAsset