2011-05-10 2 views
2

Silverlight 응용 프로그램에서 wcf 서비스를 호출하려고하는데 모델이 결과를 뷰 모델에 반환하는 방식을 이해하는 데 문제가 있습니다. 내보기 모델 내에서 나는 다음과 같은 명령이 있습니다Silverlight에서 mvvm을 사용하여 비동기 호출을 수행합니다.

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에 익숙하지 않으므로이 접근 방법에 대한 모든 도움/예가 인정 될 것입니다.

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

    } 

내가 지금은 볼 검색 결과에 시계를 배치 :

UPDATE 추가 시험에서 내가 모델에에서 INotifyPropertyChanged를 추가하고 다음과 같이 완료 이벤트를 변경, 감사 WCF의 결과로 업데이트되었습니다. 내 질문은 아직 해결되지 않았습니까? 지금 당장은 작동하는 것처럼 보이지만 다른 것을 놓치거나 모델 내에 INotify를 배치해서는 안되는 지 궁금합니다.

답변

2

나는 그것이 서비스 클래스의 추가 계층 내 WCF 서비스를 캡슐화하는 가장 좋은 것으로 나타났습니다, 감사합니다. 이를 통해 내 ViewModels의 Unit Test를보다 쉽게 ​​테스트 할 수 있습니다. 이 작업을 수행 할 때 몇 가지 패턴이 있지만 이것은 내가 사용한 가장 간단한 패턴입니다. 패턴은 서비스 호출의 정의와 일치하는 메소드를 작성하는 것이지만 서비스 호출이 완료된 후에 호출 할 수있는 조치도 포함합니다.

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 자체가 서비스와 관련된 것들을 처리 할 필요가 없습니다. 그것의 SearchResult 수집이 적절한 데이터로 채워되었을 때 모델에 의해 발사되는 SearchResultsRetrieved 이벤트와

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

:

+1

감사합니다. Joe, 이걸 보면서 명령 (내 검색을 호출하는 방법)이 내 대리자 명령과 동일하게 유지된다고 가정합니다. 주요 변경 사항은 생성 된 서비스 인터페이스를 참조하여 모델에 있습니까? – rlcrews

+0

맞습니다. 제가 만든 공개 검색() 메서드 대신 검색 명령을 계속 사용할 수 있습니다. –

+1

이것은 좋은 반응입니다. Silverlight 용 비동기를 수행하지 않더라도 통신을 서비스 계층으로 추상화하는 것이 바람직한 방식입니다. 이것에 의해, 복수의 ViewModel가 같은 서비스를 사용하기 쉬워 져 서비스 레이어 조롱이 훨씬 간단하게됩니다. –

0

나는 가능성의 라인을 따라 뭔가를 사용할 수 있습니다. 내 모델에 INotifyPropertyChanged를 구현하는 것보다 맞춤 이벤트를 선호합니다. 특히 하나 또는 몇 가지 이벤트 만 있으면 viewmodel에 전달해야합니다.

관련 문제