14

この分野での経験はほとんどなく、MVVMを使用してWCFバックエンドと通信するWPFスマートクライアントアプリを作成しており、そこにあるすべての情報から正しい決定を下すのに本当に苦労しています。これは、この分野でより経験豊富な人々がここで解決できることを願っている一連の質問につながります。

例として、画面の1つで、注文を入力したり、注文に注文明細を追加したりできます。

モデルとして何が使われていますか?

WCFサービスには、次の簡略化されたDTOがあります。

public OrderDTO
{
   string orderDetails { get; set; }
   List<OrderLineDTO> OrderLines { get; set; }
}

public OrderLineDTO
{
   int customerId { get; set; }
   int productId { get; set; }
   double quantity { get; set; }
}

そして、次の方法を持つWCFサービス:

public OrderService Order
{
    CreateOrderResponse CreateOrder(OrderDTO order) 
}

INotifyPropertyChangedWPFスマートクライアントでは、DTOへの参照がありますが、純粋に転送用であるため、明らかに実装されていません。

質問

推奨されるアプローチは、これらのDTOINotifyPropertyChangedをAutomapperなどを使用して実装するモデルに変換することですか?または、DTOをViewModelで直接モデルとして使用する必要がありますか?

ビューモデル間の通信

現在、 ViewModelsとの2つのタブ(Orderと)を持つ注文ビューがあります。[注文]タブには、含まれている顧客IDと名前があります。で顧客を選択するときは、その顧客に属する製品のみが表示されるように、顧客が選択されていることを通知する必要があります。OrderLinesOrderViewModelOrderLineViewModelComboBoxOrderViewOrderLineViewComboBox

質問

このシナリオでは、にどのようにOrderViewModel通信しますか?OrderLineViewModel

注文明細の追加とロジック/ビジネスルールの適用

サーバーレベルのアプリケーションは、PC、モバイルデバイスなどの複数のクライアントで使用されるため、すべてのビジネスルールがサーバーレベルのアプリケーションに適用されていることを確認したいと思います。例として、注文明細が追加された場合。特定の製品タイプの場合、顧客が特定の認証を持っている場合にのみ追加できます。

ただし、MVVMについて読んだことはすべて、モデルがビジネスルールと動作を適用するものであると述べています。これらの例はすべて、クライアント側でモデルを実装しています。理想的には、クライアントとサーバーの両方で同じチェックを複製したくないので、これが発生しないようにする方法を考えていました。

質問

ユーザーが無効な行を追加し、サーバーに要求を送信し、サーバーに関連するルールを適用させて応答を返すことを許可しますか?または、サーバーにリクエストを送信する前に、スマートクライアントアプリでロジックを適用しますか?

ここで概説したすべての分野で本当に良くなりたいと思っています。ご回答いただきありがとうございます。

ありがとう

アレックス

編集: これは私が前進するための最善の方法に関してもう少し明確になるのを助けてくれたので、皆さんの貢献に感謝します。すべての答えは良かったのですが、この段階での私の考えに最も合うので、私はUriの答えを受け入れることにしました。ただし、DTOのIDからViewModelのリストであるItemsSourceのSelectedItemへの変換を処理するための最良の方法はまだわかりません。コンバーターが機能する可能性があることはわかりますが、別の解決策を見つけようとしています。ありがとうアレックス

4

4 に答える 4

5

これがあなたの質問についての私の考えです:

質問: 推奨されるアプローチは、これらのDTOを、Automapperなどを使用してINotifyPropertyChangedを実装したモデルに変換することですか?または、DTOをビューモデルで直接モデルとして使用する必要がありますか?

回答:私が最も好きなアプローチは封じ込めです。私はあなたに同意します。DTOにはゲッターとセッター以外のものがあってはなりません。可能な限りクリーンに保つため、INotifyPropertyChangedを起動しないでください。また、ビューがオブジェクトモデルに直接アクセスできるべきではないと思います(他の理由がない場合は、プロパティを変更するメリットがありません)。私のアプローチの欠点は、ViewModelに余分なコードがあることですが、それだけの価値があると思います。

public class VmBase : INotifyPropertyChanged {

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void raise( string propName )
    {
        if( PropertyChanged ) {
            PropertyChanged( this, new PropertyChangedEventArgs(propName) );
        }
    }
}

public class OrderLineVm : VmBase {
    private OrderLineDTO orderLine;

    public OrderLineVm( OrderLineDTO ol ) {
        orderLine = ol;
    }

    public OrderLineVm( ) {
        orderLine = new OrderLineDTO();
    }

    int customerId {
        get { return orderLine.customerId; }
        set { orderLine.customerId=value; raise("customerId"); }
    }

    int productId {
        get { return orderLine.productId; }
        set { orderLine.productId=value; raise("productId"); }
    }

    double quantity {
       ...
    }
}

ガベージコレクションの奇跡により、OrderLineDTOは(サーバーからの場合)一度だけ作成され、必要な限り存続します。2つのパブリックコンストラクターがあります。1つはDTOを使用し(通常、オブジェクトがサーバーから送信される場合)、もう1つはクライアントで作成されます。

OrderVmの場合、これはもう少し複雑です。これは、OrderLineVm(vs. OrderLineDTO)のObservableCollection(vs. List)が必要なため、包含が機能しないためです。また、orderLinesにはゲッターしかないことに注意してください(オーダーラインを追加および削除しますが、リスト全体を変更することはありません。構築中に1回割り当てます)。

public class OrderVm : VmBase {
    private string _orderDetails;
    public string orderDetails {
        get { return _orderDetails;
        set { _orderDetails=value; raise("orderDetails"); }
    }

    private ObservableCollection<OrderLineVm> _orderLines;
    public ObservableCollection<OrderLineVm> orderLines { 
        get { return _orderLines; }
    }
}

質問: このシナリオでは、OrderViewModelはOrderLineViewModelとどのように通信しますか?

回答:コミュニケーションが必要な場合は、実際に最も簡単な方法で行う必要があります。両方のビューモデルクラスは同じレイヤーにあります。OrderVmはOrderLineVmのリストを参照します。注文するために、OrderLineVmクラスからの通信が必要な場合は、参照を保持してください。

しかし、私はコミュニケーションが必要ないことを強く主張します。ビューが適切にバインドされると、そのような通信の理由はわかりません。バインディングのModeプロパティは「双方向」である必要があるため、UIで変更されたものはすべてビューモデルで変更されます。ObservableCollectionから送信された通知のおかげで、注文明細のリストへの追加、削除はビューに自動的に反映されます。

質問:ユーザーが無効な行を追加してサーバーにリクエストを送信し、サーバーに関連するルールを適用させて応答を返すことを許可しますか?または、サーバーにリクエストを送信する前に、スマートクライアントアプリでロジックを適用しますか?

回答:サーバーだけでなく、クライアントでもデータ検証を行うのは問題ありません。重複コードを避けます-検証を実行する単一のアセンブリ(おそらくDTOを定義するアセンブリ)を用意し、このアセンブリをクライアントにデプロイします。このようにして、アプリケーションの応答性が向上し、サーバーの作業負荷が軽減されます。

明らかに、サーバー上でデータ検証を行う必要があります(セキュリティ上の理由とレースの競合のため)。クライアントでの検証に合格したにもかかわらず、サーバーがエラーを返す状況を処理する必要があります。

編集:(アレックスからのコメントをフォローアップ):

ドロップダウンリストの表示:混乱の原因は、実際には2つの独立したItemsSource(したがって2つの別個のデータコンテキスト)があることだと思います。注文ラインのリストが1つあり、各注文ラインに埋め込まれているのはProductIDのリストです。コンボボックスに入力されたアイテムです。ProductLineのプロパティであるのはSelectedItemのみです。通常、可能なProductIDのリストは、アプリケーション(または注文)に対してグローバルである必要があります。ProductIDsにフォーム全体のプロパティを設定し、それに名前を付けます(たとえば、x:Keyまたはx:Name)。次に、ComboBox要素で次のリストを参照します。

<ComboBox ItemsSource="{Binding Source={StaticResource ProductIDs}}"
          SelectedItem="{Binding Path=productId}"
          />
于 2012-01-24T13:04:29.610 に答える
1

順番にあなたの質問に答えるために...

1)プロパティが変更されたときにUIに通知する必要がない場合は、使用する必要はありませんINotifyPropertyChanged。私の意見では、モデルをビューに直接バインドできます。機能が追加されていない場合は、レイヤーを追加する必要はありません。ただし、ほとんどのアプリケーションでは、UIを介してモデルオブジェクトの状態を変更する必要があります。この場合、を実装するビューモデルオブジェクトを追加する必要がありますINotifyPropertyChanged。モデルを適応させるビューモデルを作成するか、プロパティを基になるモデルに委任するか、モデルオブジェクトの状態を同等のビューモデルにコピーすることができます。

モデルオブジェクトとビューモデルオブジェクトとして表される同じドメインオブジェクトなど、かなり類似したコードを大量に記述しないようにするために、可能な場合はコード生成を使用するようにしています。XMLを使用してモデルを記述し、T4テンプレートをcodegenに使用するのが好きです。

2)どのようOrderViewModelに通信する必要がありOrderLineViewModelますか?直接!概念は非常に密接に関連しているように聞こえますが、注文には複数の注文ラインがあると思いますか?この場合、各ビューモデルに他のモデルを参照させるだけです。2つがドメイン内で緊密に結合されている場合は、派手なメディエーターは必要ありません。

3)良い質問です!サーバーが検証を適用する必要があることに同意します。クライアントでこの検証の一部を複製するかどうかは、要件によって異なります。サーバーとの通信が高速で頻繁な場合は、ユーザーが注文を編集するときにサーバーと通信し、フィールドからフィールドへと進むときに検証を提供することで、優れたユーザーエクスペリエンスを提供できる可能性があります。ただし、多くの場合、これは実用的ではありません。クライアントアプリケーション内で単純な検証を適用することは非常に一般的ですが、サーバーがより複雑なチェック(たとえば、一意性のチェックなど)を実行できるようにします...

お役に立てば幸いです。

于 2012-01-22T22:20:41.537 に答える
0

私は「リッチクライアント」(WPF)の構築に2年の経験があります。

WPFスマートクライアントでは、DTOへの参照がありますが、純粋にトランスポート用であるため、明らかにINotifyPropertyChangedを実装していません。

間違っ
たWCFは、デフォルトですべてのDTOにINPCを自動的に実装します。
単純なビューのViewModelとしてDTOを使用するのが最善であり、より複雑なビューの場合は、コンポジション「パターン」を使用します。

ビューモデル間の通信

「ベスト」プラクティス(読む:ほとんどすべての人が行うこと)は、弱いイベントパターンを使用して、物事をゆるく結合させ続けることです。最も有名なのはPRISMライブラリのIEventAggregatorですが、いくつかの実装があります。

注文明細の追加とロジック/ビジネスルールの適用

クライアントをWebページと同じように考えてください。信頼しないでください。これは.NETコードであり、ハッキングがいかに簡単かは誰もが知っています。
そのため、WCFサービスにセキュリティチェックを実装する必要があります。

HTH、

バブ。

于 2012-01-24T13:38:05.327 に答える
0

本当の問題は、MVVMパターンにどれだけ忠実になりたいかということだと思います。

MVVMの背後にある考え方、およびMVCやMVPのような同様のパターンは、関心の分離です。私もこのトピックに苦労しましたが、パターンが何を達成しようとしているのかを詳しく調べたところ、選択が容易になりました。

MVVMには、ビュー(V)、モデル(M)、およびビューモデル(VM)の3つの懸念事項があります。かなり明白なようですよね?しかし、それぞれが本当に懸念していることと、懸念を混ぜ始めたらどうなるかを自問してください。他の場所で懸念を混ぜるときと同じように。コードの変更が難しくなります。

そのことを念頭に置いて、UIタイプを使用するプロパティを公開することにより、UIをViewModelに忍び込ませる場合を考えてみてください。これは、ダイアログ(MVVMの頭痛の主な原因)を扱うときによく見られます。サードパーティのコントロールセットを使用してアプリケーションを開発していて、UIタイプがその1つであるとします。UIマークアップを変更するだけでなく、コントロールセットを交換する場合は、複数の変更を行う必要があります(またはデザイナーに変更を依頼します)。

(私はそのような努力をしたばかりで、真のMVVMアプリはすぐにスキンを変更できましたが、他のアプリは変換に10〜25倍の時間がかかったので、これは私の心の中で新鮮です!)

この同じシナリオは、パターンの「バックエンド」にも影響します。

モデルの目的は、アプリケーションで使用している永続性メカニズムとの間でデータを転送することです。これは、Webサービス、データベース、テキストファイルなどである可能性があります。WCFがINotifyPropertyChangedなどの機能を追加したからといって、それらの使用が推奨されるわけではありません。マイクロソフトはツールの開発を行っていることを忘れないでください。これらのツールを販売するには、さまざまな状況とレベルで機能する必要があります。たとえば、RIAサービスは、手っ取り早いアプリケーションには最適ですが、実際のソリューションに適用するとすぐに機能しなくなります(少なくとも私の経験では)。

では、包含モデルを使用し、すべてのプロパティをViewModel内の状態で保持されているModelオブジェクトに委任し、モデルの性質を変更するとどうなりますか?または、モデルは必要なすべてを実行しません。事実は、ViewModelは、UIに操作に必要なものを提供するアダプターに想定されているということです。モデルと1:1の関係になることはめったにありませんが、それが起こることはわかっています。

6か月以内に、WCFではなくRESTサービスを使用することにした場合はどうなりますか?自動生成されたプロキシクラスを処理していないため、モデルでINPCをサポートしていません。UIの変更ほど具体的ではありませんが、ここでも同じ考え方が当てはまり、パターンがモデルを分離するのはそのためです。

私のアドバイスは、最初の本能に沿ってAutoMapperを使用して、Modelオブジェクトに含まれるデータをViewModelに、またはその逆にマップすることです。AutoMapperを使用すると、直面する可能性のあるインピーダンスの不一致の問題を非常に簡単に処理でき、契約の一方または他方が変更された場合に変更を加えるための単一の場所を提供します。

2

あなたが持っているのはオブジェクトモデルであり、その場合、イベントやコールバックなどを持つことは完全に合法です。OrderViewModelにはOrderLineViewModelオブジェクトのコレクションが含まれていると思います。子オブジェクトには、親(OrderViewModel)への参照を含めることができ、そこから選択した顧客をプルできます。

3

まず、ビジネスルールと検証を実装するのはモデルではなくViewModelです。第二に、そのようなルールは、ユーザーにインタラクティブな体験を提供するためにあります。ViewModelにどのルールを設定するかに関係なく、サーバーで常に整合性チェックを実行して、ユーザーが要求された操作を実行できること、およびデータが永続性に対して有効であることを確認する必要があります。

ラウンドトリップのビジネスルールの問題については、私はノーと言います。私は、クライアントアプリケーションで意味のある数のビジネスルールを適用しようとしています。1つは、ユーザーエクスペリエンスを向上させ、クライアントが必要とするネットワークトラフィックを削減することです。私が従う経験則の1つは、ユーザーが無効なオブジェクトを永続化することを決して許可しないということです。注:不正確または不完全なデータは、無効と同じではありません。無効なデータは例外を引き起こします。

于 2012-01-25T19:58:46.950 に答える