次の方法で共有


n 層アプリケーションと Entity Framework

EF4 による n 層アプリケーションの構築

Daniel Simmons

サンプル コードのダウンロード

今回の記事は、Entity Framework を使用した n 層プログラミングについてのシリーズ (https://msdn.microsoft.com/ja-jp/magazine/dd882522.aspxhttps://msdn.microsoft.com/ja-jp/magazine/ee321569.aspx を参照) の第 3 部で、特に Entity Framework (EF) と Windows Communication Foundation (WCF) を使用したカスタム Web サービスの構築について取り上げます (REST ベースのサービスなどの方法が適切な場合もありますが、このシリーズでは、カスタム Web サービスのみを扱います)。初回は、いくつか重要な設計上の考慮事項とアンチパターンについて説明しました。2 回目は、n 層アプリケーションで使用できる 4 種類のパターンについて説明しました。2 回目には、Entity Framework (EF 3.5 SP1) の初期リリースを使用して、私の言う単純なエンティティ パターンを実装する方法を示すコード サンプルを添えています。今回は、Entity Framework (EF4) の 2 番目のリリースで導入されるいくつかの機能と、それらの機能を使用して自己追跡エンティティ n 層パターンとデータ転送オブジェクト (DTO) n 層パターンを実装する方法について説明します。

単純なエンティティは、通常は n 層アプリケーションの推奨パターンではありませんが、 EF の初期リリースでは最も実用的なオプションです。ただし、EF4 では、このフレームワークを使用した n 層プログラミングのオプションが大幅に変更されています。主な新機能には、次のものがあります。

  1. 切断操作をサポートする新しいフレームワークのメソッド。たとえば、エンティティやリレーションシップの状態を新しい状態 (追加、変更など) に変更する ChangeObjectState と ChangeRelationshipState、エンティティの元の値を設定できる ApplyOriginalValues、EF によってエンティティが作成されるたびに発生する新しいイベント ObjectMaterialized などがあります。
  2. エンティティでの POCO (Plain Old CLR Objects) と外部キー値のサポート。これらの機能を使用すると、中間層のサービス実装と他の層のサービス実装との間で共有できるエンティティ クラスを作成できます。このエンティティ クラスの Entity Framework のバージョンは同じでなくてもかまいません (.NET 2.0 と Silverlight など)。外部キーを持つ POCO オブジェクトも、Java のようなプラットフォームとの相互運用性を容易にする、わかりやすいシリアル化形式を備えています。外部キーを使用することで、リレーションシップの同時実行モデルを大幅に簡素化することもできます。
  3. コード生成をカスタマイズする T4 テンプレート。T4 テンプレートは、自己追跡エンティティまたは DTO パターンを実装するクラスを生成する手段を提供します。

Entity Framework チームでは、上記の機能を使用して自己追跡エンティティ パターンをテンプレートに実装しているため、このパターンは格段にアクセスが容易になっています。一方、DTO の場合、依然として初期実装作業のほとんどが必要ですが、EF4 ではこのプロセスも簡単になりました (自己追跡エンティティ テンプレートとその他の EF 機能のいくつかは、Visual Studio 2010/.NET 4 製品に付属しているのではなく、Web ダウンロード機能 Community Technology Preview (CTP) として提供されています。今回のサンプルは、Visual Studio 2010/.NET 4 および CTP がインストールされていることを前提としています)。これらの新機能を使用する場合、説明した 4 種類のパターン (単純なエンティティ、変更セット、自己追跡エンティティ、および DTO) は、アーキテクチャ上のメリット (関心の分離/疎結合、コントラクトの強さ、効率的なワイヤ形式と相互運用性) と実装の容易さおよび市場投入までの期間を比較検討して評価します。この比較を表すグラフ上に 4 つのパターンを配置すると、図 1 のようになると思われます。


図 1 EF4 による n 層パターンの比較

どのパターンがどの状況に適しているかは、さまざまな要因によって決まります。一般論で言えば、DTO はアーキテクチャ上のメリットは多くなりますが、初期実装コストが高くなります。変更セットは、アーキテクチャ上のメリットはほとんどありませんが、実装が容易です (従来の ADO.NET の DataSet など、特定のテクノロジに利用できる場合)。

私としては、まず、自己追跡エンティティを使用し、状況を見て確実に利用できる場合は DTO に移行するという方法で、アーキテクチャ上のメリットと実装上のメリットを考慮して実用性と迅速性のバランスを探ることをお勧めします。多くの場合、自己追跡エンティティを使用すると、開発が迅速になるだけでなく、アーキテクチャ上の重要な目標の多くも実現できます。この方法は、変更セットや単純なエンティティに比べて妥協点が少なく、優れた点が多いため、個人的には、他に実用的な選択肢がない場合に限り、変更セットや単純なエンティティを使用することをお勧めします。一方 DTO は、アプリケーションのサイズが大きく複雑になるにつれて選択することが多くなります。また、自己追跡エンティティでは満たすことができない要件 (クライアントとサーバーでの変更の頻度を変える必要があるといった要件) がある場合には間違いなく最適な選択肢です。この 2 つのパターンは、ツールボックスに用意しておきたい最も重要なツールですので、それぞれについて説明しましょう。

自己追跡エンティティ

Entity Framework でこのパターンを使用するには、まず、概念エンティティを表す Entity Data Model を作成し、これをデータベースにマップします。手元にあるデータベースからモデルをリバース エンジニアリングしてカスタマイズするか、最初からモデルを作成して、対応するデータベースを生成 (EF4 の新機能) します。このモデルとマッピングが用意できたら、既定のコード生成テンプレートを自己追跡エンティティ テンプレートに置き換えます。それには、エンティティ デザイナーのデザイン サーフェイスを右クリックし、[Add Code Generation Item] (コード生成項目の追加) をクリックします。

次に、自己追跡エンティティ テンプレートをインストール済みテンプレートの一覧から選択します。この手順により、既定のコード生成が無効になり、ObjectContext を生成するテンプレートと、エンティティ クラスを生成するテンプレートの 2 つのテンプレートがプロジェクトに追加されます。コード生成を 2 つのテンプレートに分けることで、コードをエンティティ クラス用のアセンブリとコンテキスト用のアセンブリに分けられるようになっています。

この方法の最大のメリットは、Entity Framework への依存関係がないアセンブリに、エンティティ クラスを保持できることです。このようにすることで、エンティティ用のアセンブリ (または、少なくとも生成されたコード) と、これに実装したビジネス ロジックを、必要に応じて中間層とクライアントで共有できます。コンテキストは、エンティティと EF の両方に依存関係があるアセンブリに保持されます。サービスのクライアントが .NET 4 を実行している場合は、クライアント プロジェクトからエンティティ アセンブリを参照できます。クライアントが以前のバージョンの .NET または Silverlight を実行している場合は、クライアント プロジェクトから生成済みファイルへのリンクを追加し、そのプロジェクトのエンティティ ソースを (適切な CLR を対応バージョンにして) 再コンパイルすることをお勧めします。

プロジェクトの構成に関係なく、これら 2 つのテンプレートは連携して自己追跡エンティティ パターンを実装します。生成されたエンティティ クラスは単純な POCO クラスで、基本のエンティティ プロパティの保持以外の機能は、エンティティへの変更 (エンティティの全体的な状態)、同時実行トークンなど重要なプロパティの変更、エンティティ間のリレーションシップの変更を追跡する機能のみです。この追加の追跡情報は、エンティティの DataContract 定義に含まれます (したがって、WCF サービスとの間でエンティティの受け渡しがある場合、追跡情報も併せて渡されます)。

サービスのクライアント側では、エンティティがコンテキストにアタッチれていなくても、エンティティへの変更は自動的に追跡されます。生成された各エンティティには、プロパティごとに次のようなコードがあります。たとえば、状態が Unchanged であるエンティティのプロパティ値を変更すると、状態が Modified に変わります。

[DataMember]
public string ContactName
{
    get { return _contactName; }
    set
    {
            if (!Equals(_contactName, value))
            {
                _contactName = value;
                OnPropertyChanged("ContactName");
            }
    }
}
private string _contactName;

同様に、新しいエンティティがグラフに追加された場合や、グラフからエンティティが削除された場合、その情報が追跡されます。各エンティティの状態はエンティティ自体で追跡されるため、ご想像のとおり、追跡メカニズムは、複数のサービス呼び出しから取得されたエンティティを関連付ける場合でも機能します。新しいリレーションシップを確立した場合、その変更のみが追跡されます。関連するエンティティの状態は変わらず、どれも同一のサービス呼び出しから取得されたかのように扱われます。

コンテキスト テンプレートは、新しいメソッド ApplyChanges を生成済みコンテキストに追加します。ApplyChanges は、エンティティのグラフをコンテキストにアタッチして、エンティティで追跡された情報に合わせて ObjectStateManager の情報を設定します。生成済みコードは、エンティティの自己追跡情報と ApplyChanges を利用して、変更履歴の問題と同時実行の問題という、n 層アプリケーションを正しく実装するうえで最も難しい 2 つの課題を処理します。

具体例として、図 2 に単純なサービス コントラクトの例を示します。これを自己追跡エンティティと併用して、Northwind サンプル データベースに基づく n 層の発注システムを作成できます。

図 2 自己追跡エンティティ パターン用の単純なサービス コントラクト

[ServiceContract]
public interface INorthwindSTEService
{
    [OperationContract]
    IEnumerable<Product> GetProducts();

    [OperationContract]
    Customer GetCustomer(string id);

    [OperationContract]
    bool SubmitOrder(Order order);

    [OperationContract]
    bool UpdateProduct(Product product);
}

GetProducts サービス メソッドは、クライアント側で製品カタログについての参照データを取得するために使用されます。この情報は、通常、ローカルにキャッシュされ、クライアント側で頻繁には更新されません。GetCustomer は、顧客とその顧客の注文の一覧を取得します。このメソッドの実装は、次のように非常に簡単です。

public Customer GetCustomer(string id)
{
    using (var ctx = new NorthwindEntities())
    {
        return ctx.Customers.Include("Orders")
        .Where(c => c.CustomerID == id)
        .SingleOrDefault();
    }
}

これは基本的に、単純なエンティティ パターンを使用してこの種類のメソッドを実装する場合に作成するコードと同じです。違いは、返されるエンティティが自己追跡エンティティであることです。つまり、これらのメソッドを使用するためのクライアント コードも非常に単純ですが、はるかに多くの処理が可能です。

説明のためたとえば、発注プロセスで、適切な受注明細を添えて注文を作成すると同時に、顧客エンティティの一部を最新の連絡先情報に更新しなければならないとします。さらに、OrderDate が null の注文は削除するものとします (OrderDate を null にすることで注文の拒否を示すものとします)。単純なエンティティ パターンの場合、1 つのグラフ内にエンティティの追加、変更、および削除を組み込むには、各操作について複数のサービス呼び出しが必要です。または、EF の初期リリースの自己追跡エンティティのようなものを実装する場合は非常に複雑な独自のコントラクトとサービス実装が必要です。EF4 では、クライアント コードは図 3 のようになります。

図 3 自己追跡エンティティ パターン用のクライアント コード

var svc = new ChannelFactory<INorthwindSTEService>(
    "INorthwindSTEService")
    .CreateChannel();

var products = new List<Product>(svc.GetProducts());
var customer = svc.GetCustomer("ALFKI");

customer.ContactName = "Bill Gates";

foreach (var order in customer.Orders
    .Where(o => o.OrderDate == null).ToList())
{
    customer.Orders.Remove(order);
}

var newOrder = new Order();
newOrder.Order_Details.Add(new Order_Detail()
    {
        ProductID = products.Where(p => p.ProductName == "Chai")
                    .Single().ProductID,
        Quantity = 1
    });
customer.Orders.Add(newOrder);

var success = svc.SubmitOrder(newOrder);

このコードはサービスを作成し、そのサービスに対して最初の 2 つのメソッドを呼び出して製品の一覧と顧客エンティティを取得し、データベースと直接通信する 2 層の Entity Framework アプリケーションを作成する場合や、中間層にサービスを実装する場合と同様のコードを使用して、顧客エンティティのグラフを変更します (このような方法で WCF サービス クライアントを作成した経験がなくても、自己追跡エンティティ テンプレートのエンティティ クラスを再利用しているため、エンティティ プロキシを作成することなく、自動的にクライアント プロキシが作成されます。また、必要に応じて、Visual Studio の "サービス参照の追加" コマンドによって生成されたクライアントを使用することもできます)。しかしここでは、ObjectContext は使用されていません。単純にエンティティ自体を操作しています。最後に、クライアントは、SubmitOrder サービス メソッドを呼び出して、変更を中間層にプッシュしています。

もちろん、実際のアプリケーションでは、クライアントによるグラフへの変更は、おそらくなんらかの UI から実行されるため、サービス呼び出し関連の例外処理を追加することになると思いますが (ネットワークを介して通信しなければならない場合は特に重要)、図 3 のコードは基本的な原理を示しています。もう 1 つ重要なことは、新しい注文についての受注明細を作成するときに、Product エンティティ自体ではなく、ProductID プロパティのみを設定することです。これは、新しい外部キー リレーション機能の使用例です。これによって、Product エンティティのコピーではなく、ProductID のみを中間層に戻すため、ネットワーク上を送られる情報量が削減されます。

自己追跡エンティティの真価が発揮されるのは、SubmitOrder サービス メソッドの実装においてです。

public bool SubmitOrder(Order newOrder)
{
    using (var ctx = new NorthwindEntities())
    {
        ctx.Orders.ApplyChanges(newOrder);
        ValidateNewOrderSubmission(ctx, newOrder);
        return ctx.SaveChanges() > 0;
    }
}

ApplyChanges への呼び出しが、すべての処理を実行します。この呼び出しにより、エンティティから変更情報を読み取り、常時コンテキストにアタッチされているエンティティに変更が行われた場合と結果が同じになるように、読み取った変更をコンテキストに適用します。

変更を検証する

SubmitOrder 実装の中で他に注意が必要なのは、ValidateNewOrderSubmission への呼び出しです。サービス実装に追加しているこのメソッドは、ObjectStateManager を検証して、SubmitOrder への呼び出しで想定される変更のみが行われていることを確認します。

この手順は、ApplyChanges は関連するオブジェクトのグラフ全体で見つかったあらゆる変更をコンテキストに自動的にプッシュするため、非常に重要です。ここで想定されている操作が、クライアントが新しい注文を追加し、顧客情報を更新するなどの操作だけだとしても、バグのある (さらには悪意のある) クライアントがそれ以外の操作を実行しないということにはなりません。製品の価格を変更して、実際よりも発注額を少なくまたは高くしているとしたらどうなるでしょうか。検証をどのように実行するかという細かいことは、データベースに変更を保存する前に、必ず検証を行うという重大なルールに比べれば重要ではありません。このルールは、使用する n 層パターンに関係なく適用されます。

2 つ目に重要な設計上の原理は、操作ごとに独立した専用のサービス メソッドを開発することです。このように操作が独立していないと、2 層間で許可または禁止される内容を厳密に表すコントラクトを用意できず、適切に変更を検証することはできません。SubmitOrder と、独立した UpdateProduct メソッド (製品カタログを変更する権限を持つユーザーのみアクセス可能) の代わりに、SaveEntities サービスを 1 つだけ使用した場合は、このメソッドの "適用して保存" する操作を容易に実装できますが、製品の更新が許可または禁止される状況を把握する方法がないため、適切な検証は実行できません。

データ転送オブジェクト

自己追跡エンティティ パターンは、n 層アプリケーション プロセスを容易にし、専用のサービス メソッドを作成して、各メソッドを検証する場合は、非常に設計が確かなアーキテクチャになるでしょう。それでも、このパターン使用して実現できる処理には制限があります。このような制限に行き当たったときに、ジレンマから抜け出す道が DTO です。

DTO では、中間層とクライアント間で同一のエンティティ実装を共有するのではなく、サービスにデータを転送するためのみに使用されるカスタム オブジェクトを作成し、中間層とクライアント層のエンティティ実装は個別に開発します。この変更によって、主なメリットが 2 つ得られます。1 つは、サービス コントラクトを中間層側とクライアント側の実装の問題から分離できるため、中間層とクライアントの実装が変更されても、サービス コントラクトの安定性が保たれることです。もう 1 つは、ネットワーク上を転送されるデータを制御できることです。したがって、不要なデータ (または、クライアントにアクセスが許可されていないデータ) の送信を防いだり、サービスにとってより便利な形にデータを変更したりできます。一般に、サービス コントラクトは、クライアント シナリオを考慮して設計されるため、データは (複数のエンティティを 1 つの DTO にまとめるか、クライアントでは必要のないプロパティを省略するなどして) 中間層と DTO 間で形式を変更できるほか、クライアントでは DTO を直接使用できます。

ただし、上記のメリットと引き換えに、1 つ以上のオブジェクト層とマッピングを作成してメンテナンスする必要があります。発注システムの例を拡張して、新規注文を発注する目的だけのクラスを作成できます。このクラスでは、顧客エンティティのプロパティと新規注文シナリオで設定される注文のプロパティを組み合わせますが、どちらのエンティティのプロパティでも、中間層で計算されるかプロセスの別の段階で設定されるプロパティについては未設定のままとします。これにより、DTO をできる限り小さく、効率的にします。実装は、次のようになります。

public class NewOrderDTO
{
    public string CustomerID { get; set; }
    public string ContactName { get; set; }
    public byte[] CustomerVersion { get; set; }
    public List<NewOrderLine> Lines { get; set; }
}

public class NewOrderLine
{
    public int ProductID { get; set; }
    public short Quantity { get; set; }
}

これは正真正銘の 2 つのクラス (1 つは注文用、もう 1 つは受注明細用) ですが、データ サイズは最小限に抑えられています。このコード内で無関係に思える唯一の情報は、CustomerVersion フィールドです。CustomerVersion フィールドには、Customer エンティティの同時実行チェックに使用される行バージョン情報が保持されます。この情報は、Customer エンティティが既にデータベース内に存在しているため、Customer エンティティの処理に必要です。注文および明細の処理については、データベースに送信される新しいエンティティになるため、バージョン情報と OrderID は不要です。これらの情報は、変更が保存される時点で、データベースによって生成されます。

この DTO を使用できるサービス メソッドは、自己追跡エンティティ テンプレートが使用するものと同じ下位レベルの Entity Framework API を使用して目的の処理を行いますが、DTO では生成されたコードではなく、直接これらの API を呼び出す必要があります。実装は、2 つの部分から構成されます。まず、DTO の情報を基に、顧客、注文、および注文明細エンティティのグラフを作成します (図 4 参照)。

図 4 エンティティのグラフの作成

var customer = new Customer
    {
        CustomerID = newOrderDTO.CustomerID,
        ContactName = newOrderDTO.ContactName,
        Version = newOrderDTO.CustomerVersion,
    };

var order = new Order
    {
        Customer = customer,
    };

foreach (var line in newOrderDTO.Lines)
{
    order.Order_Details.Add(new Order_Detail
        {
            ProductID = line.ProductID,
            Quantity = line.Quantity,
        });
}

次に、グラフをコンテキストにアタッチし、適切な状態情報を設定します。

ctx.Customers.Attach(customer);
var customerEntry = ctx.ObjectStateManager.GetObjectStateEntry(customer);
customerEntry.SetModified();
customerEntry.SetModifiedProperty("ContactName");

ctx.ObjectStateManager.ChangeObjectState(order, EntityState.Added);
foreach (var order_detail in order.Order_Details)
{
    ctx.ObjectStateManager.ChangeObjectState(order_detail, 
       EntityState.Added);
}

return ctx.SaveChanges() > 0;

最初の行はグラフ全体をコンテキストにアタッチしますが、この処理が行われるとき、各エンティティの状態は Unchanged です。したがって、まず、ObjectStateManager に顧客エンティティの状態を Modified に変更するように指示します、ただし、Modified とするのは ContactName プロパティのみとします。これは、実際にはすべての顧客情報ではなく、DTO 内の情報しか取得されていないため、重要です。すべてのプロパティを Modified にした場合、Entity Framework は顧客エンティティの他のフィールドに null 値とゼロを保存しようとします。

次に、注文と各注文明細の状態を Added に変更して、SaveChanges を呼び出します。

おっと、検証コードはどうしたのでしょう。この場合は、シナリオに忠実な DTO であり、オブジェクトの情報をエンティティにマップしながらオブジェクトを解釈するため、検証はプロセスの進行に沿って実行されます。製品エンティティは操作しないため、このコードが誤って製品の価格を変更することはありません。これは、かなり間接的ですが、DTO パターンのまた別のメリットの 1 つです。それでも検証作業を実行する必要があります。ただし、パターンによって強制されるの 1 レベルの検証のみです。多くの場合は、値やその他のビジネス ルールの検証をコードに追加する必要があります。

また、同時実行の例外を適切に処理することも考慮する必要があります。前述のとおり、顧客エンティティのバージョン情報が DTO に含まれているため、他のユーザーが同じ顧客を変更している場合、適切に同時実行の問題が検出されるように設定できます。さらに完成したサンプルでは、この例外を WCF エラーにマップしてクライアントが競合を解決できるようにするか、例外をキャッチして、競合処理のための自動ポリシーのようなものを適用します。

注文を変更する機能など、別の操作を追加してサンプルをさらに拡張する場合、そのシナリオ専用の、適切な情報のみを設定した別の DTO を作成します。このオブジェクトは今回示した NewOrderDTO と似ていますが、注文および注文明細エンティティの OrderID プロパティと Version プロパティのほか、サービス呼び出しによる更新を許可する各プロパティを設定します。このサービス メソッド実装は、前出の SubmitOrderDTO メソッドにも似ていて、DTO データをスキャンし、対応するエンティティ オブジェクトを作成して、それぞれの状態を状態マネージャーに設定してから、データベースに変更を保存します。

注文更新メソッドを自己追跡エンティティとデータ転送オブジェクトの両方で実装する場合は、自己追跡エンティティ実装ではエンティティを再利用し、新しい発注メソッドとほぼ同じサービス実装コードを共有することになります。唯一の違いは検証コードですが、これについても部分的には共有する可能性があります。ただし、DTO 実装では、2 つのサービス メソッドのそれぞれに専用のデータ転送オブジェクト クラスが必要です。メソッド実装は同様のパターンに従いますが、共有できるコードはあったとしてもほとんどありません。

最前線からのヒント

注意すべき点および理解すべき点についていくつかヒントを紹介します。

  • 必ず自己追跡エンティティ テンプレートの生成済みエンティティ コードをクライアントに再利用する。 Visual Studio の "サービス参照の追加" やその他のツールによって生成されたプロキシ コードを使用する場合、ほとんどの部分は適切のように見えますが、エンティティが実際にはクライアント上での各自の変更を追跡しません。
  • 各サービス メソッドの Using ステートメント内に新しい ObjectContext を作成して、メソッドが戻る前に破棄されるようにする。 この手順は、サービスのスケーラビリティにとって重要です。これにより確実に、データベース接続が複数のサービス呼び出しで開かれたままにならないようにし、特定の操作が使用した一時的な状態が、操作の終了時点でガーベージ コレクションによって回収されるようにします。Entity Framework は自動的にメタデータやその他の必要な情報をアプリケーション ドメインにキャッシュし、ADO.NET はデータベース接続をプールするため、毎回コンテキストを作成し直すことでパフォーマンスが良くなります。
  • 可能な限り、新機能の外部キー リレーションシップを使用する。 この機能を使用することで、エンティティ間の変化するリレーションシップが大幅に作成しやすくなります。独立した関連付け (Entity Framework の初期リリースでの唯一のリレーションシップ) を使用すると、エンティティに対して実行される同時実行チェックとは別に、リレーションシップに対して同時実行チェックが実行され、これらのリレーションシップの同時実行チェックを明示的に無効にすることができませんでした。このため、サービスはリレーションシップの元の値を保持して、リレーションシップが変更される前にコンテキストに設定する必要ががりました。しかし、外部キー リレーションシップを使用すると、リレーションシップはエンティティの 1 プロパティに過ぎず、エンティティが同時実行チェックに合格したら、その他のチェックは不要です。外部キー値を変更するだけで、リレーションシップを変更できます。
  • グラフを ObjectContext にアタッチするときは、EntityKey の競合に注意する。 たとえば、DTO と、新しく追加されたエンティティを表すグラフのパーツを使用していて、それらのエンティティはエンティティ キー値がデータベース内で生成されるためにまだ設定されていない場合、Attach メソッドを呼び出して追加されたエンティティの状態を 
Added に変更するのではなく、まず AddObject メソッドを呼び出してエンティティのグラフ全体を追加してから、状態が Added でないエンティティを目的の状態に変更します。AddObject ではなく最初に Attach を呼び出した場合、Entity Framework ではすべてのエンティティの状態を Unchanged にする必要があると見なされ、現在のキー値が最終的なエンティティ キー値として扱われます。特定の種類の複数のエンティテイが同じキー値 (たとえば 0) を使用していた場合、Entity Framework は例外をスローします。Added 状態のエンティティから処理を開始することで、Entity Framework では Added 状態のエンティティに一意キー値が設定されていることは想定しないため、この問題を回避できます。
  • サービス メソッドからエンティティを返す場合は、自動遅延読み込み (EF4 の新機能) を無効にする。 無効にしないと、シリアライザーは遅延読み込みを実行し、データベースからさらにエンティティを取得しようとします。このため、意図しているよりも多くのデータが返されるか (エンティティが完全に接続されている場合、データベース全体がシリアライズされる可能性があります)、おそらく、シリアライザーがデータを取得する前にコンテキストが破棄されるため、エラーが返されます。自己追跡エンティティでは遅延読み込みが既定で有効にされていませんが、DTO ソリューションを作成する場合は、これは注意が必要な点です。

最後に

Entity Framework の .NET 4 リリースでは、設計が確かなアーキテクチャの n 層アプリケーションを格段に作成しやすくなります。ほとんどのアプリケーションでは、まず自己追跡エンティティ テンプレートを使用することをお勧めします。これにより、プロセスが容易になり、ほとんどのコードを再利用できます。サービスとクライアント間で変更の頻度が異なる場合や、ワイヤ形式を完全に制御する必要がある場合は、データ転送オブジェクト実装に移行する必要があります。どのパターンを使用する場合でも、アンチパターンとパターンの示す重要な原理を常に念頭におき、データは保存前に必ず検証するようにしてください。

Daniel Simmons は、マイクロソフトの Entity Framework チームのアーキテクトです。