делать асинхронные вызовы, используя mvvm в silverlight - PullRequest
2 голосов
/ 10 мая 2011

Я пытаюсь позвонить в службу wcf с помощью своего приложения silverlight, и у меня возникают некоторые проблемы с пониманием того, как модель возвращает результат обратно в модель представления.В моей модели представления у меня есть следующая команда:

 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), однако я не могу получить эту коллекцию для обновления / уведомления модели представления результата.Я сгенерировал и выполнил аналогичный тест, используя только метод и фиктивный класс, и он, кажется, работает, поэтому я подозреваю, что проблема связана с асинхронным вызовом / многопоточностью.У меня есть INotifyPropertyChanged в ViewModel для синхронизации View и ViewModel.Нужно ли также реализовывать INotifyPropChng внутри модели?Я новичок в mvvm, так что любая помощь / пример того, как мне следует подойти к этому, будет оценена.

Спасибо,

ОБНОВЛЕНИЕ В дальнейшем тестировании я добавил INotifyPropertyChanged вмодель и изменила событие «Завершено» следующим образом:

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

    }

Размещение часов в результатах поиска Теперь я вижу, что они обновляются с результатами WCF.Мой вопрос все еще вокруг, это правильный подход?Кажется, это работает прямо сейчас, однако мне любопытно, если я что-то упускаю или мне не следует помещать INotify в Модель.

Спасибо,

Ответы [ 2 ]

2 голосов
/ 10 мая 2011

Я обнаружил, что лучше всего инкапсулировать мои сервисы WCF в дополнительный уровень классов обслуживания.Это позволяет мне легче проводить модульное тестирование моих моделей представления.При этом есть несколько шаблонов, хотя это самый простой, который я использовал.Шаблон предназначен для создания метода, который соответствует определению вызова службы, но также содержит действие, которое может быть вызвано после завершения вызова службы.

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 не нужно заботиться о тех вещах, которые конкретно относятся к Сервису.

0 голосов
/ 10 мая 2011

Я бы, вероятно, использовал что-то вроде:

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

Когда событие SearchResultsRetrieved вызывается Моделью, когда ее коллекция SearchResults заполнена соответствующими данными. Я предпочитаю иметь пользовательские события, а не реализовывать INotifyPropertyChanged на моих моделях, особенно если есть только одно или несколько событий, которые необходимо передать в модель представления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...