22

MVVM で構築された WPF アプリケーションに自己追跡エンティティを渡す WCF クライアントがあります。アプリケーション自体には動的インターフェースがあります。ユーザーは、自分の役割や実行しているタスクに応じて、作業領域に表示するオブジェクトを選択できます。

私の自己追跡エンティティにはかなりの数のナビゲーション プロパティがあり、それらの多くは必要ありません。これらのオブジェクトの一部は非常に大きくなる可能性があるため、これらのプロパティは要求があった場合にのみロードしたいと考えています。

私のアプリケーションは次のようになります。

[WCF] <---> [ClientSide Repository] <---> [ViewModel] <---> [View]

私のモデルは自己追跡エンティティです。クライアント側リポジトリは、要求元の ViewModel にモデルを返す前に、必要に応じて LazyLoad メソッドをフックします。すべての WCF サービス呼び出しは非同期です。つまり、LazyLoad メソッドも非同期です。

LazyLoad の実際の実装で問題が発生しています。ここに私が思いついたオプションがあります。

編集 - コードサンプルを削除して、これを読みやすく理解しやすくしました。見たい場合は、以前のバージョンの質問を参照してください

オプション A

Getter で WCF サーバーからモデルのプロパティを非同期的に LazyLoad する

良い:オンデマンドでのデータの読み込みは非常に簡単です。XAML のバインドによってデータが読み込まれるため、コントロールが画面上にある場合、データは非同期に読み込まれ、そこにあるときに UI に通知されます。そうでない場合、何もロードされません。たとえば<ItemsControl ItemsSource="{Binding CurrentConsumer.ConsumerDocuments}" />、データをロードしますが、インターフェイスのドキュメント セクションが存在しない場合は何もロードされません。

悪い例:空のリストを返すため、このプロパティを開始する前に他のコードで使用することはできません。たとえば、ドキュメントが読み込まれていない場合、次の呼び出しは常に false を返します。

public bool HasDocuments 
{ 
    get { return ConsumerDocuments.Count > 0; }
}

オプション B

必要に応じて手動で呼び出してデータをロードする

良い:実装が簡単 -メソッドLoadConsumerDocumentsSync()LoadConsumerDocumentsAsync()メソッドを追加するだけ

悪い例: Bindings で使用する場合を含め、データにアクセスする前にデータをロードすることを忘れないでください。これは簡単に思えるかもしれませんが、すぐに手に負えなくなる可能性があります。たとえば、各 ConsumerDocument には UserCreated と UserLastModified があります。拡張機能、電子メール、チーム、ロールなどの追加のユーザー データを表示する ToolTip を使用して UserModel を定義する DataTemplate があります。そのため、ドキュメントを表示する ViewModel では、 を呼び出してからLoadDocuments、それらをループして and を呼び出す必要がLoadConsumerModifiedありLoadConsumerCreatedます。それも続くかもしれません...その後、私はしなければなりませLoadUserGroupsLoadUserSupervisor. また、 a のようなものがプロパティをUser持ち、 aがプロパティを持つ循環ループのリスクを冒します。Groups[]GroupUsers[]

オプション C

これまでのところ私のお気に入りのオプションは... プロパティにアクセスする 2 つの方法を作成することです。1 つの同期と 1 つの非同期。バインドは Async プロパティに対して行われ、すべてのコードで Sync プロパティが使用されます。

良い:データは必要に応じて非同期に読み込まれます - まさに私が望むものです。これらの追加のプロパティ/メソッドを生成するために T4 テンプレートを変更するだけでよいため、追加のコーディングはそれほど多くありません。

悪い例:同じデータにアクセスする方法が 2 つあると、効率が悪く、混乱を招きます。Consumer.ConsumerDocumentsAsyncの代わりにいつ使用する必要があるかを覚えておく必要がありますConsumer.ConsumerDocumentsSync。WCF サービス呼び出しが複数回実行される可能性もあります。これには、IsConsumerDocumentsLoaded などのすべてのナビゲーション プロパティに追加の IsLoaded プロパティが必要です。

オプション D

非同期ロードをスキップし、セッターですべてを同期的にロードします。

良い:非常にシンプルで、余分な作業は必要ありません

悪い:データの読み込み時に UI がロックされます。これはいらない。

オプション E

SOの誰かに、これを行う別の方法があることを教えてもらい、コードサンプルを教えてもらいます:)

その他の注意事項

一部の NavigationProperties は、オブジェクトをクライアントに返す前に WCF サーバーに読み込まれますが、それ以外はコストが高すぎてそれを行うことができません。

オプション C の Load イベントを手動で呼び出すことを除いて、これらはすべて T4 テンプレートを介して実行できるため、私が行うコーディングはほとんどありません。私がしなければならないことは、クライアント側のリポジトリで LazyLoad イベントをフックし、それを適切なサービス呼び出しに向けることだけです。

4

8 に答える 8

3

まず最初に、この問題に対する明確な解決策を読者に提供する必要があると言わなければなりません。User.Documents プロパティにバインドするときに DependecyProperties が非同期にロードされることは問題ありませんが、副作用ベースにかなり近いです。解決。ビューでのそのような動作が問題ないと言う場合は、その意図について残りのコードを非常に明確にしておく必要があります。これにより、データにアクセスしようとしている方法を確認できます-何かの詳細な名前付け(メソッド、クラス名、smthそうしないと)。

したがって、古い .AsSynchronized() アプローチに近いソリューションを使用して、デコレータ クラスを作成し、各プロパティにプライベート/保護された AsyncLoad および SyncLoad メソッドを提供できると思います。デコレータ クラスは、各 lazyloadable の Sync または Async バージョンになりますクラス、より適切なものは何でも。

クラスを Sync デコレータで装飾すると、遅延読み込み可能な各クラスも Sync デコレータでラップされるため、同期クラス バージョンで SynchUser(User).Documents.Count を問題なく使用できるようになり、SynchUser(user .SyncDocuments(Documents).Documents プロパティのオーバーロードされたバージョンでカウントが遅れ、sync getter 関数が呼び出されます。

同期バージョンと非同期バージョンの両方が同じオブジェクトで動作するため、このアプローチでは、プロパティを変更する場合に参照されていない他のオブジェクトを変更することはありません。

あなたの仕事は、魔法のような「美しくシンプルな」方法で解決できるように聞こえるかもしれませんが、私はそれができるとは思わないか、これよりも簡単ではないと思います.

これが機能しない場合でも、クラスの同期または非同期バージョンが使用されているかどうかをコードで区別する明確な方法が必要であるか、コードベースを維持するのが非常に困難であると100%確信しています。

于 2011-05-08T07:31:11.393 に答える
1

オプション Aが解決策になるはずです。

LoadingStatusという名前のプロパティを 1 つ作成して、データがロードされているか、ロードがまだロードされていないことを示します。データを非同期にロードし、それに応じて LoadingStatus プロパティを設定します。

各プロパティの読み込みステータスを確認し、データが読み込まれていない場合は、関数を呼び出してデータを読み込みます

于 2011-05-11T10:39:40.207 に答える
1

ここでBinding.IsAsyncライブラリ プロパティが役立つでしょうか。

編集:少し拡張します..最初の使用時にWCFサービスを呼び出す遅延ロードされた同期プロパティを持っています。次に、非同期バインディングにより、UI がブロックされなくなります。

于 2011-06-06T13:08:48.467 に答える
1

私が思いついた解決策は、自己追跡エンティティの T4 テンプレートを変更して、以下に示す変更を行うことでした。これを読みやすくするために実際の実装は省略されていますが、プロパティ/メソッド名はすべてが何をするかを明確にする必要があります。

古い T4 生成のナビゲーション プロパティ

[DataMember]
public MyClass MyProperty { get; set;}

private MyClass _myProperty;

新しい T4 生成ナビゲーション プロパティ

[DataMember]
internal MyClass MyProperty {get; set;}
public MyClass MyPropertySync {get; set;}
public MyClass MyPropertyAsync {get; set;}

private MyClass _myProperty;
private bool _isMyPropertyLoaded;

private async void LoadMyPropertyAsync();
private async Task<MyClass> GetMyPropertyAsync();
private MyClass GetMyPropertySync();

私は、同じ私有財産を指すプロパティの 3 つのコピーを作成しました。内部コピーは EF 用です。おそらくそれを取り除くことができますが、EFはその名前のプロパティを想定しているため、そのままにしておくのが最も簡単であり、EFを修正して新しいプロパティ名を使用するよりもそのままにしておく方が簡単です。クラスの名前空間以外で使用したくないため、これは内部的なものです。

プロパティの他の 2 つのコピーは、値が読み込まれるとまったく同じ方法で実行されますが、プロパティの読み込み方法が異なります。

LoadMyPropertyAsync()単純に実行される非同期バージョンは実行されますGetMyPropertyAsync()。これには 2 つのメソッドが必要でした。async修飾子を getter に置くことができず、非非同期メソッドから呼び出す場合は void を返す必要があるからです。

同期バージョンが実行GetMyPropertySync()され、それがGetMyPropertyAsync()同期的に実行されます

これはすべて T4 によって生成されるため、WCF サービスからエンティティを取得するときに、非同期遅延読み込みデリゲートを接続する以外に必要なことはありません。

私のバインディングはプロパティの Async バージョンを指し、他のコードはプロパティの Sync バージョンを指しており、どちらも追加のコーディングなしで正しく機能します。

<ItemsControl ItemsSource="{Binding CurrentConsumer.DocumentsAsync}" />

CurrentConsumer.DocumentsSync.Clear();
于 2011-05-10T19:18:48.493 に答える
0

私の見方では、ViewModelは利用可能なデータがあるかどうかを認識する必要があります。データのフェッチ中にデータがないと意味のないUI要素を非表示または無効にして、データが到着したときに表示することができます。

一部のデータを読み込む必要があることを検出したため、UIを「待機」モードに設定し、非同期フェッチを開始して、データが受信されたら待機モードを解除します。おそらく、ViewModelに、関心のあるオブジェクトの「LoadCompleted」イベントをサブスクライブさせることによって。

(編集)各モデルオブジェクトの状態を追跡することにより、過度の負荷や循環依存を回避できます:Unloaded / Loading/Loaded。

于 2011-05-06T20:56:43.437 に答える
0

私の頭の中には2つの考えがあります。

IQueryable<>1) サービスに応答を実装しWCFます。そして、IQueryable<>パターンを使用して DB までたどります。

2) クライアント リポジトリで、プロパティにゲッターを設定しConsumerDocumentsてデータを取得します。

private IEnumerable<ConsumerDocuments> _consumerDocuments;

public IEnumerable<ConsumerDocuments> ConsumerDocuments
{
    get
    {
        return _consumerDocuments ?? (_consumerDocuments = GetConsumerDocuments() );
    }
}
于 2011-05-06T19:08:02.743 に答える
0

これがオプション E です。

データを非同期にロードします。完全なオブジェクトをゆっくりと埋めるバックグラウンド スレッドで、最初のフェッチ キューに物事を入れます。また、バックグラウンドでデータをロードする必要があるメソッドは、ロードの終了時にブロックされるようにします。(ブロックて、必要なデータの優先度が高いことをバックグラウンド スレッドに通知してもらい、次にデータを取得して、できるだけ早くブロックを解除できるようにします。)

これにより、可能な場合はすぐに反応する UI が得られ、何が読み込まれたかを考えずにコードを書くことができ、ほとんど問題なく動作します。1 つの落とし穴は、データの読み込み中にブロック呼び出しを行うことがありますが、あまり頻繁に行わないことを願っています。もしそうなら、最悪の場合、オプション C のようなものに劣化し、データのフェッチをブロックする機能と、データが存在するかどうかをポーリングする機能の両方があります。ただし、ほとんどの場合、あまり心配する必要はありません。

免責事項: 私は個人的に Windows を使用しておらず、ほとんどの時間を UI から離れたバックエンドの作業に費やしています。アイデアが気に入ったら、ぜひ試してみてください。しかし、実際には、動的 Web ページの舞台裏での AJAX 呼び出しよりも複雑なものについて、この戦略に従っていません。

于 2011-05-07T00:49:23.380 に答える