【问题标题】:Calling DAL from ViewModel asynchronously从 ViewModel 异步调用 DAL
【发布时间】:2013-06-19 11:13:43
【问题描述】:

我正在使用 MVVM-light 构建复合 WPF 应用程序。我有使用 MEF 将 ViewModel 注入其中的视图:

DataContext = App.Container.GetExportedValue<ViewModelBase>(
                ViewModelTypes.ContactsPickerViewModel);

此外,我对每个视图(屏幕和用户控件)都有 ViewModel,其中构造函数通常如下所示:

private readonly ICouchDataModel _couchModel;

    [ImportingConstructor]
    public ContactsPickerControlViewModel(ICouchDataModel couchModel)
    {
        _couchModel = couchModel;
        _couchModel.GetContactsListCompleted+=GetContactsListCompleted;
        _couchModel.GetConcatcsListAsync("Andy");
    } 

目前,我遇到了一些性能问题。一切都很缓慢。 我有两种相关的问题

  1. 异步调用 DAL 方法的正确方法是什么(访问我的 couchdb)?等待/异步?任务?因为目前我必须围绕每个操作编写很多包装器(OnBegin,OnCompletion),所以我有 GetAsyncResult 方法,它用 ThreadPool.QueueUserWorkItem ,Action 等做一些疯狂的事情。 我希望有更优雅的调用方式

  2. 目前,我的应用程序中有一些屏幕,每个屏幕上都有不同的自定义用户控件,其中一些需要来自 DB 的相同数据(或稍作更改)。 问题:在他们之间共享数据源的正确方法是什么?我主要是查看数据,而不是编辑。

    示例:在屏幕 A 上:我有联系人下拉列表用户控件 (UC1) 和联系人详细信息用户控件 (UC2)。在每个用户控件中,他们的 ViewModel 都在调用 DAL:

    _couchModel.GetConcatcsListAsync("Andy");

完成后,我将结果数据分配给属性:

List<ContactInfo> ContactsList = e.Resuls; 

ContactsList 绑定到 UC1 中 DropDownListBox 的 ItemsSource。同样的故事也发生在 UC2 中。所以我最终得到了 2 次完全相同的 DB 调用。 此外,如果我去有 UC1 的屏幕 B,当我将从屏幕 A 转到屏幕 B 时,我将再次调用 DB。

进行这些交互的正确方法是什么?获取数据并将其绑定到 UC。

谢谢。

【问题讨论】:

    标签: wpf architecture mvvm-light data-access-layer


    【解决方案1】:

    Ad.1
    我认为您可以简单地使用 Task.Factory 来异步调用代码(因为您可以摆脱 OnBegin、OnCompletion),或者如果您需要更大的灵活性,则可以使方法异步。

    广告。 2
    这样做的好方法(在我看来)是创建 DatabaseService(单例),它将被注入到构造函数中。在 DatabaseService 中,您可以实现一些逻辑来确定是要刷新集合(调用 DAL)还是返回相同的集合(它将是某种缓存)。

    然后您可以直接调用 DatabaseService 而不是 DAL,DatabaseService 将决定如何处理此调用(从 DB 获取集合或返回相同或稍作修改的当前集合)。


    编辑:
    DatabaseService 将简单地在 ViewModel 之间共享对象集合。
    也许“DBCacheService”这个名字会更合适(您可能只会将它用于特殊任务作为缓存集合)。

    我不知道您的架构,但基本上您可以将该服务放在您的客户端应用程序中,因此计划是:

    1. 创建DatabaseService.cs

      [Export(typeof(IDatabaseService))]
      public class DatabaseService : IDatabaseService
      {
          private List<object> _contacts = new List<object>();
      
          public async Task<List<object>> GetConcatcsList(string name)
          {
              if (_contacts.Count == 0)
              {
                  //call DAL to get it
                  //_contacts = await Task.Factory.StartNew(() => dal.GetContactsList(name));
              }
              else
              {
                  //refresh list if required (there could be some condition)                
              }
      
              return _contacts;
          }
      }
      
    2. 将 IDatabaseService 添加到 ViewModel 的构造函数中。

    3. 调用 IDatabaseService 而不是 DAL。

    如果您选择异步版本的 DatabaseService,那么您需要使用 await 并将您的方法更改为异步。您也可以同步执行它并像这样调用它(只要您希望它是异步的):

    Task.Factory.StartNew(() => 
    {
        var result = dbService.GetContactsList("Andy");
    });
    

    编辑2:

    在 Task 中调用 awaitable 方法:

    Task.Factory.StartNew(async () => 
    {
        ListOfContacts = await _CouchModel.GetConatcsList ("Andy");
    });
    

    【讨论】:

    • 例如,使用 Tasks 而不是 async 的权衡是什么?您是否有实施此类 DatabaseService 的示例?基本上 DatabaseService 只是数据库周围的另一个缓存,不是吗?
    • 我已经编辑了我的答案。异步/等待使用任务。我的意思是不同的——你可以使方法同步,然后异步调用它们(只要你需要)创建任务或使它们异步并使用等待(但它也需要使调用方法异步)。
    • 好的,谢谢。但我仍然不明白等待/异步的东西。我怎样才能让我的方法可以等待?因为目前我正在尝试: if (_friendList.Count == 0) { //call DAL 获取它 await Task.Run(() =>_manager.GetFriendList(name) ); } 并且它不起作用
    • 我在我的回答中修改了那行。究竟是什么不起作用?
    • 感谢您对我的支持,您能说明如何从 ViewModel 调用 GetConcatcsList 吗? ListOfContacts = 等待 _CouchModel.GetConatcsList ("Andy"); - 告诉我只能在异步方法中使用 await。但例如,我不想让我的 ViewModel 的构造函数异步。它不会起作用。
    猜你喜欢
    • 1970-01-01
    • 2017-10-18
    • 2016-08-12
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多