【问题标题】:making async calls using mvvm in silverlight在 silverlight 中使用 mvvm 进行异步调用
【发布时间】:2011-05-10 12:49:09
【问题描述】:

我正在尝试使用我的 silverlight 应用程序调用 wcf 服务,但我在理解模型如何将结果返回给视图模型时遇到了一些麻烦。在我的视图模型中,我有以下命令:

 public DelegateCommand GetSearchResultCommand
    {
        get
        {
            if (this._getSearchResultCommand == null)
                this._getSearchResultCommand = new DelegateCommand(GetSearchResultCommandExecute, CanGetSearchResultsCommandExecute);

           return this._getSearchResultCommand;
        }

    }

private void GetSearchResultCommandExecute(object parameter)
    {

       this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
    }
/// <summary>
    /// Bindable property for SearchResults
    /// </summary>
    public ObservableCollection<QueryResponse> SearchResults
    {
        get
        {
            return this._SearchResults;
        }
        private set
        {
            if (this._SearchResults == value)
                return;

            // Set the new value and notify
            this._SearchResults = value;
            this.NotifyPropertyChanged("SearchResults");
        }
    }

然后在我的模型中我有以下代码

public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery)
    {   
        //return type cannot be void needs to be a collection
        SearchClient sc = new SearchClient();
        //******
        //TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
        // sc.Endpoint.Address = (clientProxy); 
        //******

        sc.QueryCompleted += new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted);
        sc.QueryAsync(new Query { QueryText = searchQuery });
        return LastSearchResults;
   }

    void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
    {
        ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
        results.Add(e.Result);
        this.LastSearchResults = results;
    }

当我在模型中插入断点时,我看到查询正在执行的位置并在模型中返回结果(this.LastSearchResults = results)但是我似乎无法让这个集合更新/通知视图模型结果。我只使用一个方法和虚拟类生成并运行了一个类似的测试,它似乎可以工作,所以我怀疑问题是由于异步调用/线程造成的。我在 ViewModel 中有 INotifyPropertyChanged 来同步 View 和 ViewModel。我还需要在模型中实现 INotifyPropChng 吗?我是 mvvm 的新手,所以任何关于我应该如何解决这个问题的帮助/示例都将不胜感激。

谢谢,

更新 在进一步的测试中,我将 INotifyPropertyChanged 添加到模型中,并将 Completed 事件更改如下:

 void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
    {
        ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
        results.Add(e.Result);
        //this.LastSearchResults = results;
        SearchResults = results;

    }

关注搜索结果我现在看到它已根据 WCF 的结果进行了更新。我的问题仍然存在,这是正确的方法吗?它现在似乎可以工作,但是我很好奇我是否遗漏了其他东西,或者我是否不应该将 INotify 放在模型中。

谢谢,

【问题讨论】:

    标签: silverlight mvvm c#-4.0


    【解决方案1】:

    我发现最好将我的 WCF 服务封装在额外的服务类层中。这使我可以更轻松地对我的 ViewModel 进行单元测试。执行此操作时有几种模式,尽管这是我使用过的最简单的模式。该模式是创建一个与服务调用的定义相匹配的方法,但也包含一个可以在服务调用完成后调用的 Action。

    public class Service : IService
    {
        public void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply)
        {   
            //return type cannot be void needs to be a collection
            SearchClient sc = new SearchClient();
            //******
            //TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
            // sc.Endpoint.Address = (clientProxy); 
            //******
    
            sc.QueryCompleted += (s,e) =>
            {
              ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
              results.Add(e.Result);
              reply(results);
            };
    
            sc.QueryAsync(new Query { QueryText = searchQuery });
       }
    }
    

    您还可以提供 ViewModel 可以使用的接口。这使得单元测试更加容易,尽管是可选的。

    public interface IService
    {
        void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply);
    }
    

    您的 ViewModel 将如下所示:

    public class MyViewModel : INotifyPropertyChanged
    {
        private IService _service;
    
        public MyViewModel()
          : this(new Service())
        { }
    
        public MyViewModel(IService service)
        {
          _service = service;
    
          SearchResults = new ObservableCollection<QueryResponse>();
        }
    
        private ObservableCollection<QueryResponse> _searchResults
        public ObservableCollection<QueryResponse> SearchResults
        {
          get { return _searchResults; }
          set
          {
            _searchResults = value;
            NotifyPropertyChanged("SearchResults");
          }
        }
    
        public void Search()
        {
          _service.GetSearchResults("abcd", results =>
          {
            SearchResults.AddRange(results);
          });
        }
    
        protected void NotifyPropertyChanged(string property)
        {
          var handler = this.PropertyChanged;
          if(handler != null)
            handler(new PropertyChangedEventArgs(property));
        }
    }
    

    像这样将服务调用封装到另一个类中的另一个原因是,它可以为诸如日志记录和错误处理之类的事情提供一个单一的位置。这样一来,您的 ViewModel 本身就不需要处理与服务特别相关的那些事情。

    【讨论】:

    • 谢谢乔,在看这个时,我假设命令(我如何调用搜索)与我的委托命令保持不变?主要变化是在模型中通过引用创建的服务接口?
    • 没错 - 除了我创建的公共 Search() 方法,您仍然可以使用您的搜索命令。
    • 这是一个很好的回应。即使您没有为 Silverlight 执行异步,将通信抽象到服务层也是我的首选方法。这也使得多个 ViewModel 更容易使用相同的服务,并使服务层模拟更加简单。
    【解决方案2】:

    我可能会使用以下内容:

    public class ViewModel : INotifyPropertyChanged
    {
        private readonly IModel model;
        private readonly DelegateCommand getSearchResultsCommand;
    
        public DelegateCommand GetSearchResultsCommand
        {
            get { return getSearchResultsCommand; }
        }
        public ObservableCollection<QueryResponse> SearchResults
        {
            get { return model.SearchResults; }
        }
    
        public ViewModel(IModel model)
        {
            this.model = model;
            this.model.SearchResultsRetrieved += new EventHandler(model_SearchResultsRetrieved);
    
            this.getSearchResultsCommand = new DelegateCommand(model.GetSearchResultCommandExecute, model.CanGetSearchResultsCommandExecute);
        }
    
        private void model_SearchResultsRetrieved(object sender, EventArgs e)
        {
            this.NotifyPropertyChanged("SearchResults");
        }
    
    }
    
    public interface IModel
    {
        event EventHandler SearchResultsRetrieved;
    
        void GetSearchResultCommandExecute(object parameter);
        bool CanGetSearchResultsCommandExecute(object parameter);
    
        ObservableCollection<QueryResponse> SearchResults { get; }
    }
    

    当模型的 SearchResults 集合已填充适当的数据时,模型会触发 SearchResultsRetrieved 事件。我更喜欢自定义事件而不是在我的模型上实现 INotifyPropertyChanged,尤其是当只有一个或几个事件需要与视图模型通信时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-16
      • 2012-10-24
      • 1970-01-01
      • 2020-02-08
      • 1970-01-01
      • 1970-01-01
      • 2017-03-17
      • 2020-08-28
      相关资源
      最近更新 更多