オブジェクトの追加、変更、および削除 (Entity Framework)

オブジェクト コンテキスト内のオブジェクトは、データ ソース内のデータを表現するエンティティ型のインスタンスです。オブジェクト コンテキスト内でオブジェクトを変更、作成、および削除でき、Object Services によってこれらのオブジェクトに対する変更が追跡されます。SaveChanges メソッドが呼び出されると、Object Services によって、データ ソースに対して挿入、更新、または削除ステートメントを実行するための対応するコマンドが生成されて実行されます。詳細については、「変更の保存と同時実行制御の管理 (Entity Framework)」を参照してください。

たとえば、SalesOrderHeader オブジェクトおよび SalesOrderDetail オブジェクトのコレクションを返すクエリを実行するとします。このコレクションを列挙し、次の操作を実行することができます。

  • 注文の ShipDate プロパティを変更する。

  • DeleteObject メソッドを呼び出して特定のアイテムを削除する。

  • Add メソッドを呼び出して行アイテムを注文に追加する。

  • オブジェクトの変更をデータ ソースに保存するために、オブジェクト コンテキストに対して SaveChanges メソッドを呼び出す。

次の例は、オブジェクト コンテキスト内のオブジェクトに対するさまざまな変更を示しています。

Dim order As SalesOrderHeader = _
context.SalesOrderHeader.Where( _
        "it.SalesOrderID = @id", New ObjectParameter( _
         "id", orderId)).First()

' Change the status and ship date of an existing order.
order.Status = 1
order.ShipDate = DateAndTime.Today

' Load items for the order, if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
    order.SalesOrderDetail.Load()
End If

' Delete the first item in the order.
context.DeleteObject(order.SalesOrderDetail.First())

' Create a new item using the static Create method
' and add it to the order.
order.SalesOrderDetail.Add( _
    SalesOrderDetail.CreateSalesOrderDetail( _
    1, 0, 2, 750, 1, CDec(2171.2942), 0, 0, Guid.NewGuid(), _
    DateAndTime.Today))

' Save changes in the object context to the database.
Dim changes As Integer = context.SaveChanges()
SalesOrderHeader order =
    context.SalesOrderHeader.Where
    ("it.SalesOrderID = @id", new ObjectParameter(
     "id", orderId)).First();

// Change the status and ship date of an existing order.
order.Status = 1;
order.ShipDate = DateTime.Today;

// Load items for the order, if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
    order.SalesOrderDetail.Load();
}

// Delete the first item in the order.
context.DeleteObject(order.SalesOrderDetail.First());

// Create a new item using the static Create method 
// and add it to the order.
order.SalesOrderDetail.Add(
    SalesOrderDetail.CreateSalesOrderDetail(0,
    0, 2, 750, 1, (decimal)2171.2942, 0, 0,
    Guid.NewGuid(), DateTime.Today));

// Save changes in the object context to the database.
int changes = context.SaveChanges();

オブジェクトの追加

データをデータ ソースに挿入する場合は、エンティティ型のインスタンスを作成してオブジェクトをオブジェクト コンテキストに追加する必要があります。新しいオブジェクトを追加するには、まず null 値をサポートしないすべてのプロパティを設定する必要があります。エンティティ型の新しいインスタンスを作成するには、エンティティ型の CreateObjectName 静的メソッドの使用を検討してください。エンティティ データ モデル ツールでは、エンティティ型の生成時に各クラスにこのメソッドが含まれます。この Create メソッドは、オブジェクトのインスタンスを作成し、クラスの null に設定できないすべてのプロパティを設定するために使用します。このメソッドには、CSDL ファイル内で Nullable="false" 属性が適用されているすべてのプロパティに対するパラメータがあります。

次の例では、CreateSalesOrderHeader 静的メソッドを使用して、SalesOrderHeader クラスの新しいインスタンスを作成します。

' Create a new SalesOrderHeader using the static 
' CreateSalesOrderHeader method.
Dim order As SalesOrderHeader = _
    SalesOrderHeader.CreateSalesOrderHeader( _
    1, Convert.ToByte(1), DateTime.Now, DateTime.Today.AddMonths(2), _
    Convert.ToByte(1), False, String.Empty, customer.ContactID, shipMethod, _
    0, 0, 0, 0, Guid.NewGuid(), DateTime.Now)
// Create a new SalesOrderHeader using the static 
// CreateSalesOrderHeader method.
SalesOrderHeader order = SalesOrderHeader.CreateSalesOrderHeader(0,
    Convert.ToByte(1), DateTime.Now, DateTime.Today.AddMonths(2),
    Convert.ToByte(1), false, string.Empty, customer.ContactID, shipMethod, 
    0, 0, 0, 0, Guid.NewGuid(), DateTime.Now);

詳細については、「静的 Create メソッドを使用してオブジェクトを作成する方法 (Entity Framework)」を参照してください。

新しいオブジェクトをオブジェクト コンテキストに追加するには、AddObject メソッドを呼び出すか、型指定された ObjectContext に対していずれかの AddToEntitySetName メソッドを呼び出します。また、既存の EntityCollection に追加することによってオブジェクトをオブジェクト コンテキストに追加することもできます。オブジェクト コンテキストにアタッチされている EntityCollection に対して Add メソッドを呼び出す場合、追加するオブジェクトは同じ ObjectContext に追加されます。同様に、新しいオブジェクト インスタンスを EntityReferenceValue に設定することによってオブジェクトを追加できます。

ナビゲーション プロパティでオブジェクト間のリレーションシップを定義します。オブジェクトがオブジェクト コンテキスト内の他のオブジェクトに関連する場合は、このプロパティを設定することをお勧めします。たとえば、新しい SalesOrderDetail オブジェクトの SalesOrderHeader リレーションシップ プロパティを、行アイテムが属する注文のインスタンスに設定します。オブジェクト コンテキストで他のオブジェクトに関連する新しいオブジェクトを作成する場合、次のいずれかの方法でオブジェクトを追加します。

  • 一対多または多対多のリレーションシップの場合は、EntityCollection に対して Add を呼び出し、関連オブジェクトを指定します。

  • 一対一または多対一のリレーションシップの場合は、EntityReferenceValue プロパティを関連オブジェクトに設定します。

  • AddObject メソッドを呼び出して新しいオブジェクトをオブジェクト コンテキストに追加し、上記の 2 つの方法のいずれかを使用してリレーションシップを定義します。

新しいオブジェクトを追加する際は、次の点に注意してください。

  • SaveChanges が呼び出される前に、AddObject メソッドを使用して追加されるすべての新しいオブジェクトに対して、Object Services によって一時的なキー値が生成されます。SaveChanges が呼び出された後、このキー値は、新しい行の挿入時にデータ ソースで割り当てられる ID 値に置き換わります。

  • エンティティのキー値がデータ ソースで生成されない場合は、一意の値を割り当てる必要があります。2 つのオブジェクトに対してユーザーが同じキー値を指定していると、SaveChanges の呼び出し時に InvalidOperationException が発生します。その場合は、一意の値を割り当てて操作を再試行する必要があります。

  • オブジェクト間のリレーションシップを定義して SaveChanges を呼び出すと、データ ソース内に外部キー値が エンティティ フレームワーク によって自動的に設定されます。ただし、挿入、更新、および削除を実行するストアド プロシージャにエンティティがマップされている場合、外部キー値は自動的に設定されません。その場合は、外部キーに対応するプロパティを関連オブジェクトの正しい値に適切に設定する必要があります。詳細については、「ストアド プロシージャのサポート (Entity Framework)」を参照してください。

オブジェクトの変更

オブジェクトのスカラ プロパティ、複合プロパティ、またはナビゲーション プロパティを変更して SaveChanges を呼び出すと、更新がデータ ソースに送信されます。EntityReference の値の変更や、EntityCollection からのオブジェクトの削除など、ナビゲーション プロパティの変更を行うと、オブジェクト間のリレーションシップが変更されます。詳細については、「オブジェクト間のリレーションシップを変更する方法 (Entity Framework)」を参照してください。

Object Services は、IEntityChangeTracker のインスタンスを使用して ObjectContext にアタッチされているオブジェクトに対する変更を追跡します。追跡対象の各オブジェクトには、IEntityChangeTracker のインスタンスが 1 つずつあります。NoTracking に設定された MergeOption がクエリで使用されていない限り、クエリは Unchanged 状態のオブジェクトを返します。エンティティ データ モデル ツールでは、エンティティ型のプロパティごとにプロパティ setter での変更追跡メソッドの呼び出しが生成されます。次に、SalesOrderHeader クラスの Status プロパティのプロパティ setter の例を示します。

Set(ByVal value As Byte)
    Me.OnStatusChanging(value)
    Me.ReportPropertyChanging("Status")
    Me._Status = Global.System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value)
    Me.ReportPropertyChanged("Status")
    Me.OnStatusChanged()
End Set
set
{
    this.OnStatusChanging(value);
    this.ReportPropertyChanging("Status");
    this._Status = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
    this.ReportPropertyChanged("Status");
    this.OnStatusChanged();
}

ReportPropertyChanging メソッドと ReportPropertyChanged メソッドによって、プロパティの変更が IEntityChangeTracker に報告されます。エンティティ データ モデル (EDM) でカスタム データ クラスを使用している場合は、プロパティの変更を報告して Object Services による追跡を有効にする必要もあります。詳細については、「カスタム データ クラス内の変更の報告 (Entity Framework)」を参照してください。

プロパティ setter が呼び出されるたびに、オブジェクトの状態が Unchanged から Modified に変更されます。これは、設定されている値が現在の値と同じである場合でも発生します。AcceptAllChanges メソッドが呼び出されると、状態は Unchanged に戻ります。既定では、AcceptAllChangesSaveChanges 操作中に呼び出されます。

エンティティ データ モデル ツールでは、OnPropertyChanging および OnPropertyChanged という部分メソッドのペアも生成されます。これらのメソッドはプロパティ setter 内で呼び出されます。部分クラス内のこれらのメソッドを拡張して、プロパティの変更時にカスタム ビジネス ロジックを挿入します。詳細については、「プロパティの変更時にビジネス ロジックを実行する方法 (Entity Framework)」を参照してください。

オブジェクトを変更する際は、次の点に注意してください。

  • 複合オブジェクトのスカラ プロパティまたは複合プロパティを変更すると、最上位レベルのエンティティ オブジェクトの状態が Modified に変更されます。

  • オブジェクトが Detached 状態の場合、変更は追跡されません。オブジェクトは、NoTracking マージ オプションを使用するクエリによって返された場合、または Detach の呼び出しによって ObjectContext からデタッチされた後、この状態になります。

  • 2 つのオブジェクト間のリレーションシップを変更するには、EntityReference 値を新しいオブジェクトに割り当てます。この場合、SaveChanges を呼び出すと、データ ソース内の外部キー値が エンティティ フレームワーク によって自動的に更新されます。ただし、挿入、更新、および削除を実行するストアド プロシージャにエンティティがマップされている場合、外部キー値は自動的に更新されません。その場合は、外部キーに対応するプロパティを新しいリレーションシップの正しい値に適切に設定する必要があります。詳細については、「ストアド プロシージャのサポート (Entity Framework)」を参照してください。

オブジェクトの削除

ObjectContext に対して DeleteObject メソッドを呼び出すと、指定したオブジェクトが削除対象としてマークされます。行は、SaveChanges が呼び出されるまではデータ ソースから削除されません。

オブジェクトを削除する際は、次の点に注意してください。

  • オブジェクトを削除すると、他のオブジェクトに対するリレーションシップもすべて削除されます。

  • 2 つのオブジェクトの間に制約されたリレーションシップがある場合、親オブジェクトを削除すると、子オブジェクトもすべて削除されます。この結果は、リレーションシップに対するアソシエーションの CascadeDelete プロパティを有効にすることと同じです。詳細については、「参照に関する制約 (Entity Framework)」を参照してください。

  • クエリによって返されるオブジェクトが 1 つ以上の他のオブジェクトに関連する場合、クエリは常に、オブジェクトを削除しやすくするためにこれらの関連オブジェクトに関する情報を返します。場合によっては、オブジェクトを削除しようとすると、この既存の情報が原因で UpdateException が発生することがあります。たとえば、リレーションシップを定義するアソシエーションで、アソシエーションの親 End<OnDelete Action="Cascade" /> 要素が指定されている場合に、この例外が発生します。発生した場合は、DeleteObject メソッドを呼び出す前に、関連オブジェクトを明示的に読み込んでください。

  • 削除済みのオブジェクトに対して、DeleteObject メソッドを再度呼び出すことができます。

詳細については、「オブジェクトを追加、変更、および削除する方法 (Entity Framework)」を参照してください。

特定の EntitySet でのオブジェクトの作成

1 つのエンティティ型が複数のエンティティ セットに属する場合があります。たとえば、データベース内に同じスキーマを持つ 2 つのテーブルがある場合について考えてみます。このような状況としては、バックアップ プロセスの効率を向上させるためにデータをパーティション分割した場合などが考えられます。たとえば、顧客データが Customer テーブルと CustomerArchive テーブルにパーティション分割されているとします。CustomerArchive は、Customer と同じスキーマですが、6 か月以上発注していない顧客に対して使用されます。Customer は毎晩バックアップされ、CustomerArchive は週に一度だけバックアップされます。マッピングの観点から見ると、CustomerCustomerArchive は別々のエンティティ セットに属する必要があります。エンティティ フレームワーク では、1 つのエンティティ型が 1 つまたは複数のエンティティ セットに存在できるようにすることで、このシナリオをサポートしています。詳細については、「エンティティ セット (EDM)」を参照してください。

1 つのエンティティ型が複数のエンティティ セットに存在する場合、Object Services では、その型の新しいインスタンスを特定のエンティティ セットに追加できます。そのためには、AddObject メソッドを呼び出してオブジェクトをオブジェクト コンテキストに追加するときに、entitySetName の値を指定する必要があります。詳細については、「オブジェクトを特定のエンティティ セットに追加する方法 (Entity Framework)」を参照してください。

エンティティ データ モデル ツールでは、ObjectContext に対して AddToEntitySetName メソッドも生成されます (概念モデルで定義したエンティティ セットごとに 1 つのメソッドが生成されます)。これらのメソッドによって AddObject が呼び出され、特定のメソッドの EntitySetName 値が渡されます。これらのメソッドを使用して、オブジェクトを特定のエンティティ セットに追加します。

参照

処理手順

Multiple-Entity-Sets-per-Type でモデルを定義する方法 (Entity Framework)