12

Entity Framework 5 を使用して最初の MVC 4/Razor Web アプリを構築しており、設計上の決定を下す前に少し宿題をしています。

EF オブジェクトが から派生していることがわかりますEntityObject。これには、多くの有用なベスト プラクティスが構築されているように見えます。特に、オプティミスティック コンカレンシー処理が含まれます。つまり、2 人のユーザーがメープル ストリート 123 番地のジェーン ドウのレコードを同時にロードした場合、最初のユーザーが名前をジェーン スミスに変更し、2 番目のユーザーが住所をメープル ストリート 321 番地に変更した場合、両方の変更を簡単にマージできます。 2 番目のユーザーが最初のユーザーと同じフィールドを変更しようとすると、エラーが発生します。

一方、軽量のデータ転送オブジェクトを作成して、サーバーとクライアントの間でデータを渡し、MVC フレームワークのモデルとして、またはモデルとして機能することは、かなり標準的な方法のようです。これは、クライアントへのトラフィックを最小限に抑えるのに最適ですが、並行性チェックを台無しにします。

だから私は、DTO を使用する理由を疑問視しています。DTO を使用する理由は何ですか? MVC モデルとして、または MVC モデル内で使用するのはどれほど悪いことEntityObjectですか? 上記で説明したように、オプティミスティックな同時実行処理を有効にするために他にどのようなソリューションを提案しますか?

4

5 に答える 5

10

=回答としてコメントを投稿する=

EFオブジェクトは、数バージョン前からのPOCOです(どちらかはわかりません)。「EntityObject」が必要な場合は、何らかのアダプターを使用する必要があります(アプリケーションの移行を容易にするアダプターがあると思いますが、新しいプロジェクトの一部として使用することはお勧めしません)。

モデルクラスにEF関連のメソッドがある場合、そうすることは本当に悪いことです。しかし、EF5はすべきではありません。4.1以降、まさにその理由でEntityObjectを拡張する代わりにプロキシを使用していると思います。これは、モデルとして使用することをお勧めします。

.ttファイルと生成された.csファイルを確認してください。それらはプレーンなPOCOです。インターフェイスも基本クラスもありません。エンティティフレームワークからオブジェクトを取得し、オブジェクトのタイプを確認すると、のようなものが見つかりますSystem.Data.Entity.DynamicProxies.Employee_5E43C6C196[...]。これは、プロキシによって生成されたクラスです。ただし、まったく同じことを実行しても、(dbContext.Configuration.ProxyCreationEnabled = false;)の前にデータベースコンテキスト構成を変更すると、優れたEmployeeエンティティを獲得できます。

したがって、元の質問に答えるには、EF POCOをモデルとして使用することは完全に受け入れられます/良い習慣ですが、それらを非永続オブジェクトとして使用するようにしてください。

追加情報

DDDの概念と、リポジトリなどのDDD準拠のパターンの実装、または使用しやすいと思われるものを検討する必要があります。

これらのエンティティを、永続的または非永続的に、ビューで直接使用しないでください。

AutoMapperについて読んで、生活を楽にする必要があります(リポジトリまたはスタンドアロンとうまく調和します)。これにより、ProxyEmployee->Employee->ViewModelおよびその逆からの転送が容易になります。

EFエンティティの恐ろしい使用例:

return View(dbContext.employees.First());

EFエンティティの悪い#1使用法の例:

Employee e = dbContext.employees.First();
return View(new Employee { name = e.name, [...] });

EFエンティティの悪い#2使用法の例:

Employee e = dbContext.employees.First();
return View(new EmployeeViewModel{ employee = e });

EFエンティティのok使用法の例:

Employee dbEmploye = dbContext.employees.First();
Employee e = new Employee { name = dbEmploye.name, [...] };
return View(new EmployeeViewModel { employee = e });

EFエンティティの適切な使用例:

Employee e = dbContext.employees.First();
EmployeeViewModel evm = Mapper.Map<Employee, EmployeeViewModel>(e);
return View(evm);

EFエンティティのすばらしい使用例:

Employee e = employeRepository.GetFirstEmployee();
EmployeeViewModel evm = Mapper.Map<Employee, EmployeeViewModel>(e);
return View(evm);

チャックノリスはそれをどのように行うか:

return View(EmployeeViewModel.Build(employeRepository.GetFirstEmployee()));
于 2012-10-11T13:39:32.033 に答える
7

EntityObjectをビューに直接渡すときにのみ悪い点が表示されます。

  • 過剰投稿や大量割り当てを防ぐために、ホワイトリストまたはブラックリストを手動で作成する必要があります
  • ビューから余分なデータを誤って遅延読み込みすることが非常に簡単になり、その結果、一部の N+1 問題が発生します。
  • 私の個人的な意見では、モデルはビューに表示される情報に非常に似ている必要があり、ほとんどの場合 (基本的な CRUD のものを除く)、ビューには複数の情報が含まれています。EntityObject
于 2012-10-11T11:46:17.127 に答える
3
On the other hand, it seems pretty standard practice to create lightweight Data Transfer Objects to pass data between the server and the client, and which serve as or in models for the MVC framework. This is great for ensuring minimal traffic to the client, but it screws up concurrency checking

ここでは、ワイヤを介してブラウザに流れるものについて話しているようです。この意味で、コントローラーで EntityObject クラスを使用する場合でも、データをクライアントにレンダリングし、より基本的な形式で送り返す必要があります。そのため、クライアントを使用すると、同時実行サポートは実際には関係ありません。

于 2012-10-11T11:46:33.560 に答える
1

Dtos にはいくつかの利点がありますが、計画内容によって異なります。

それらをフラットに設計すると、次の場合により良く簡単になります。

  • ビュー モデルへのマッピング
  • これらの dto オブジェクトをドメイン オブジェクトとしてキャッシュすると、グラフが大きくなり、キャッシュに入れるのに適していない可能性があります。Dtos を使用すると、何をどのようにキャッシュするかについて適切な粒度を得ることができます
  • API署名を簡素化し、プロキシされたオブジェクトの予期しない遅延読み込みを防ぎます
  • ワイヤーを介してデータを送信するには、ストリッパー パターンについてをお読みください

しかし、これが必要ない場合は、何もしなくてもかまいません。

于 2012-10-11T11:45:38.357 に答える
0

これまでのすべての回答が次のことを示しているように見えることに基づいて:

  • s をクライアントにプッシュするのは悪い習慣EntityObjectです。主な理由は、膨大な量の関連データを誤って遅延読み込みするリスクがあるためです。
  • DTO は楽観的並行性チェックを行う能力を台無しにします

私なりの解決策を提案しますので、それが良いアイデアかどうかについてコメントをお待ちしています。

プレーンなバニラ DTO をクライアントに渡す代わりに、抽象GenericDTO<T>クラスを作成しTますEntityObjectGenericDTOProperty<T>また、2 つのプロパティを持つクラスを作成します。

public class GenericDTOProperty<T> {
  public T LoadValue { get; internal set; }
  public T Value { get; set; }
}

ここで、EntityObject「ID」と「名前」のプロパティを持つ「Customer」という名前があるとします。次のような DTO クラスを作成します。

public class CustomerDTO : GenericDTO<Customer> {
  public GenericDTOProperty<int> ID;
  public GenericDTOProperty<string> Name;
}

ここで投機的なコードにあまり触れずに、GenericDTOリフレクションを使用して との間で値をコピーし、ロード時EntityObjectに を設定し、LoadValue保存時に変更されていないことを検証するコードをクラスにカプセル化します。IDすべてのテーブルに 1 つの ID フィールドがあることが確実な場合は、さらに巧妙にプロパティを基本クラスに配置できます。

これは合理的なパターンのように思えますか? それは十分に単純に思えます-ほとんど単純すぎます...それはおそらくそれに何か問題があるのではないかと心配しています。

于 2012-10-11T12:41:15.773 に答える