2011-06-08 49 views
4

我們目前正在設計一個使用WPF的系統,它將使用Web服務與其他系統進行通信。MVVM和服務對象

我們試圖儘可能少的層次和映射(通過堅持最簡單的工作來降低成本)。

我們將使用MVVM模式。

所以我們必須有一個視圖模型。

問題是我們可以使用從服務返回的對象作爲Model對象,還是應該將這些對象映射到客戶端中定義的模型對象?

回答

1

您不必創建自己的模型圖層,但:如果服務被更改,您將不得不在反映引用該模型的所有圖層中的更改。如果你現在創建一個自己的模型層,你會更安全,但更多的工作。像往常一樣:更多的工作將最終爲您節省更多未來的工作。如果你擁有這項服務,並且你確信你永遠不會改變它(哈哈),你不需要模型層。

當然,它取決於服務檢索對象是否完全符合您的需求。如果你使用巨大的物體1,2屬性,它可能既不是一個很好的選擇...

1

是的,你可以使用WCF服務對象是你的模型層(我們是),雖然像@fantasticfix說,如果你的WCF服務器對象更改必須搜索引用來修復它們。另外,如果要將整個模型公開到視圖並綁定到模型的屬性,則需要確保WCF服務器對象實現INotifyPropertyChanged

3

雖然你可以使用你的模型從服務返回的數據對象,有幾個原因,以避免這種情況:

  1. 你可以最終將數據綁定要求(INotifyPropertyChangedINotifyCollectionChangedIDataErrorInfo等)到你的服務數據對象中。
  2. 特別是在服務可能被多個應用程序使用的情況下,獨立於彼此演進服務和WPF應用程序變得更加困難。

您應該考慮在您的模型中使用Repository Pattern將您的服務通信封裝爲一個或多個Web服務存儲庫。 Web服務存儲庫允許您集中服務的訪問邏輯,併爲單元測試提供替代點,併爲您提供緩存之前服務操作結果的機會。

存儲庫充當數據和不同域中的操作之間的橋樑。存儲庫向數據源發出適當的查詢,然後通過使用Data Mapper模式在表示之間進行轉換,將結果集映射到業務實體。

您的視圖模型將使用服務存儲庫來檢索或保留信息,存儲庫處理對服務的調用以及將數據的服務表示映射到將與數據綁定的模型特定類。

您可以更進一步併爲服務存儲庫定義通用接口,以便您可以在應用程序可能執行的基於CRUD的操作的基礎上實現特定於服務的存儲庫。

例通用服務存儲庫接口:

/// <summary> 
/// Describes a service repository that separates the logic that retrieves, persists and maps data to the 
/// domain model from the business logic that acts on the domain model. 
/// </summary> 
/// <typeparam name="TChannel">The type of channel produced by the channel factory used by the repository.</typeparam> 
/// <typeparam name="TMessage">The type of data contract to map to the domain entity of type <typeparamref name="T"/>.</typeparam> 
/// <typeparam name="T">The type of domain entity mediated by the repository.</typeparam> 
/// <typeparam name="TKey">The type of the key that uniquely identifies domain entities within the repository.</typeparam> 
public interface IServiceRepository<TChannel, TMessage, T, TKey> : IRepository<T, TKey> 
    where T : class 
{ 
    /// <summary> 
    /// Occurs when the repository transitions from one state to another. 
    /// </summary> 
    event EventHandler<StateChangedEventArgs> StateChanged; 

    /// <summary> 
    /// Gets the configuration name used for the service endpoint. 
    /// </summary> 
    /// <value> 
    /// The name of the endpoint in the application configuration file that is used 
    /// to create a channel to the service endpoint. 
    /// </value> 
    string EndpointConfigurationName 
    { 
     get; 
    } 

    /// <summary> 
    /// Gets the current state of the service repository. 
    /// </summary> 
    /// <value> 
    /// The current <see cref="CommunicationState"/> of the service repository. 
    /// </value> 
    CommunicationState State 
    { 
     get; 
    } 
} 

例通用倉庫接口:

/// <summary> 
/// Describes a repository that separates the logic that retrieves, persists and maps data to the domain model 
/// from the business logic that acts on the domain model. 
/// </summary> 
/// <typeparam name="T">The type of domain entity mediated by the repository.</typeparam> 
/// <typeparam name="TKey">The type of the key that uniquely identifies domain entities within the repository.</typeparam> 
public interface IRepository<T, TKey> 
    where T : class 
{ 
    /// <summary> 
    /// Occurs when a repository action has been completed. 
    /// </summary> 
    event EventHandler<RepositoryActionCompletedEventArgs<T>> Completed; 

    /// <summary> 
    /// Occurs when a repository action fails to execute. 
    /// </summary> 
    event EventHandler<RepositoryActionFailedEventArgs<T>> Failed; 

    /// <summary> 
    /// Gets a value indicating if the repository has been disposed of. 
    /// </summary> 
    /// <value> 
    /// <see langword="true" /> if the repository has been disposed of; otherwise, <see langword="false" />. 
    /// </value> 
    bool IsDisposed 
    { 
     get; 
    } 

    /// <summary> 
    /// Adds a new <paramref name="entity"/> to the data source layer. 
    /// </summary> 
    /// <param name="entity">The entity of type <typeparamref name="T"/> to insert into the data source layer.</param> 
    /// <param name="callback"> 
    /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is insert into the data source layer. 
    /// </param> 
    /// <returns> 
    /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on. 
    /// </returns> 
    /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception> 
    IRepository<T, TKey> Add(T entity, Action<T, Exception> callback = null); 

    /// <summary> 
    /// Retrieves all entities of type <typeparamref name="T"/> from the data source layer. 
    /// </summary> 
    /// <param name="callback"> 
    /// The optional <see langword="delegate"/> method that will be executed after all entities of type <typeparamref name="T"/> are retrieved from the data source layer. 
    /// </param> 
    /// <returns> 
    /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on. 
    /// </returns> 
    IRepository<T, TKey> Get(Action<IEnumerable<T>, Exception> callback = null); 

    /// <summary> 
    /// Retrieves an entity of type <typeparamref name="T"/> from the data source layer that 
    /// matches the specified <paramref name="key"/>. 
    /// </summary> 
    /// <param name="key">The unique identifier of the entity of type <typeparamref name="T"/> to retrieve from the data source layer.</param> 
    /// <param name="callback"> 
    /// The optional <see langword="delegate"/> method that will be executed after an entity of type <typeparamref name="T"/> that matches the specified <paramref name="key"/> is retrieved from the data source layer. 
    /// </param> 
    /// <returns> 
    /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on. 
    /// </returns> 
    IRepository<T, TKey> Get(TKey key, Action<T, Exception> callback = null); 

    /// <summary> 
    /// Removes an existing <paramref name="entity"/> from the data source layer. 
    /// </summary> 
    /// <param name="entity">An entity of type <typeparamref name="T"/> to delete from the data source layer.</param> 
    /// <param name="callback"> 
    /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is removed from the data source layer. 
    /// </param> 
    /// <returns> 
    /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on. 
    /// </returns> 
    /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception> 
    IRepository<T, TKey> Remove(T entity, Action<T, Exception> callback = null); 

    /// <summary> 
    /// Updates an existing <paramref name="entity"/> within the data source layer. 
    /// </summary> 
    /// <param name="entity">The entity of type <typeparamref name="T"/> to update within the data source layer.</param> 
    /// <param name="callback"> 
    /// The optional <see langword="delegate"/> method that will be executed after the <paramref name="entity"/> is updated within the data source layer. 
    /// </param> 
    /// <returns> 
    /// The <see cref="IRepository{T, Tkey}"/> object that this method was called on. 
    /// </returns> 
    /// <exception cref="ArgumentNullException">The <paramref name="entity"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception> 
    IRepository<T, TKey> Update(T entity, Action<T, Exception> callback = null); 
} 
1

我使用MVVM一個WPF應用程序在沒有服務和模型檢索使用來自同一解決方案的內存中的dll服務。在這樣的地方取模型中的服務是我們總量控制的環境中,它是有道理的沒有任何額外的層。因此,我們只有模型(其中由在內存中的DLL服務arefetched),視圖模型對象和視圖。

但是,如果你正在使用Web服務,我認爲該服務將獨立發展其使用的。如果您的服務合同明天改變,您不會希望您的應用程序崩潰或產生大量維護成本。所以最好的做法是重新創建使用WCF模型的UI端自己的模型,然後在它的工作。因此,如果合同變更明天的變化最有可能是圍繞測繪WCF模型的邏輯,你的UI模型,而不是超越。這個額外的層面是值得的。

如果您的服務在您的控制之下並且您的應用程序足夠簡單,那麼您可以嘗試將這些服務模型用作MVVM模型。

不要試圖將ViewModel和Model合併成一個模型(通過在模型本身中實現INotifyPropertyChange)。我嘗試了這一點,隨着應用程序複雜性的增長,它很快就會變得混亂。而且,這樣你的模型將會與所有的Command實現一起膨脹。它們應該只包含業務邏輯,並且不要承擔太多照顧UI邏輯的責任。

+0

你知道如何插入這樣一個服務層的例子嗎? – 2013-01-11 20:25:26