EF5コードファーストで作成したかなり標準的な多対多の関係を持つ2つのエンティティがあります。これらはServiceとServiceItemです。 ServiceエンティティにはServiceItemのコレクションが含まれ、ServiceItemにはServicesのコレクションが含まれます。どちらのエンティティの基本プロパティにも問題なくデータを作成、変更、保存できます。 ServiceItemをServiceに、またはServiceをServiceItemに追加しようとすると、機能しているように見えますが、何も保存されません。クロスキーを持つServiceItemServiceテーブルを含め、すべての適切なデータベーステーブルが作成されていることを確認しました。アイテムを追加しても、データベースのServiceItemServiceテーブルにエントリがありません。エラーはなく、他のすべては完全に機能しているようです。
私は少し困惑していて、いくつかの助けを使うことができます。以下はクラスです。
サービスクラス。
public class Service
{
//Default constructor
public Service()
{
//Defaults
IsActive = true;
ServicePeriod = ServicePeriodType.Monthly;
ServicePeriodDays = 0;
ServiceItems = new Collection<ServiceItem>();
}
public int ServiceID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<ServiceItem> ServiceItems { get; set; }
public string TermsOfService { get; set; }
public ServicePeriodType ServicePeriod { get; set; }
public int ServicePeriodDays { get; set; }
public bool IsActive { get; set; }
}
ServiceItemクラス。
public class ServiceItem
{
public ServiceItem()
{
IsActive = true;
}
public int ServiceItemID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<Service> Services { get; set; }
public string UserRole { get; set; }
public bool IsActive { get; set; }
}
これは、この問題をデバッグしようとしたときに行ったFluentマッピングです。このマッピングを追加する前後に同じ問題が発生しました。
public DbSet<Service> Services { get; set; }
public DbSet<ServiceItem> ServiceItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>()
.HasMany(p => p.ServiceItems)
.WithMany(r => r.Services)
.Map(mc =>
{
mc.MapLeftKey("ServiceItemID");
mc.MapRightKey("ServiceID");
mc.ToTable("ServiceItemService");
});
}
これは、Service.ServiceItemsコレクションに2〜3個のServiceItemを含むServiceアイテムを保存するために使用するコードです。 ServiceItemが適切なコレクションに含まれていることを注意深く確認しました。
db.Entry(dbService).State = EntityState.Modified;
db.SaveChanges();
DbServiceオブジェクトは影響を受けていないようです。 ServiceItemsは引き続き適切なコレクションにありますが、ServiceItemServiceデータベーステーブルは更新されません。どんなアドバイスでも大歓迎です。
-ありがとう
何も起こらないことが期待されます。
変更または追加したいのは、エンティティService
とServiceItem
の間の関係です。ただし、エンティティの状態をModified
に設定して関係を操作することはできません。これは、スカラープロパティと複雑なプロパティのみを更新し、ナビゲーションプロパティ(=関係)は更新しません。 (たとえば、Service
エンティティの状態をModified
に設定すると、Service.Title
やService.Description
などが変更済みとしてマークされ、これらのプロパティがデータベースに確実に保存されます。 。ただし、Service.ServiceItems
の内容は関係ありません。)
状態をModified
に設定してrelationshipを変更できる唯一の例外は、Foreign Key Associationsです。これらは、モデルエンティティで公開されている外部キープロパティを持つ関連付けであり、1対多または1対1の関連付けでのみ発生します。多対多の関係は常に独立した関連付けです。つまり、エンティティ内に外部キープロパティを持つことはできません。 (FKは結合テーブルにありますが、結合テーブルはエンティティではなく、モデルクラスから「非表示」になっているためです。)
多対多の関連付けの関係を直接操作する方法はありますが、ObjectContext
とそのRelationshipManager
に移動する必要があります。これは、私の意見では、かなり高度で注意が必要です。
多対多の関連付けとの間で関係エントリを追加および削除する通常の簡単な方法は、コレクションにアイテムを追加したり、コレクションからアイテムを削除したりすることですエンティティがアタッチされている間コンテキストに。 EFの変更追跡メカニズムは、SaveChanges
を呼び出すと、行った変更を認識し、適切なINSERT、UPDATE、およびDELETEステートメントを生成します。
正確な手順は、Service
および/またはServiceItem
も新しいエンティティとして保存するか、既存のエンティティ間の関係のみを追加するかによって異なります。次にいくつかの例を示します。
service
を挿入し、すべてのserviceItem
sを挿入し、エンティティ間の関係も結合テーブルに挿入する必要があります。
using (var context = new MyContext())
{
var service = new Service();
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.Services.Add(service);
context.SaveChanges();
}
オブジェクトグラフの「ルート」service
を追加するだけで十分です。これは、EFがグラフ内の他のすべてのエンティティがコンテキストにアタッチされていないことを認識し、データベースに挿入する必要があると想定するためです。
service
はすでに存在するため、挿入しないでください。すべてのserviceItem
を挿入し、エンティティ間の関係も結合テーブルに挿入する必要があります。
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
EFは、ここで(SaveChanges
が呼び出されたとき)service
がアタッチされていることを認識しますが、他のエンティティはアタッチされていません。 service
のINSERTは発生しませんが、serviceItem1/2
はリレーションシップエントリと一緒にINSERTされます。
service
はすでに存在するため、挿入しないでください。すべてのserviceItem
は既に存在するため、挿入しないでください。ただし、エンティティ間の関係は、結合テーブルに挿入する必要があります。
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem { ServiceItemID = 23 };
context.ServiceItems.Attach(serviceItem1);
var serviceItem2 = new ServiceItem { ServiceItemID = 37 };
context.ServiceItems.Attach(serviceItem2);
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
完全を期すために:どのように削除既存のエンティティ間の関係?
using (var context = new MyContext())
{
var service = context.Services
.Include(s => s.ServiceItems) // load the existing Items
.Single(s => s.ServiceID == 15);
var serviceItem1 = service.ServiceItems
.Single(s => s.ServiceItemID == 23); // query in memory, no DB query
var serviceItem2 = service.ServiceItems
.Single(s => s.ServiceItemID == 37); // query in memory, no DB query
service.ServiceItems.Remove(serviceItem1);
service.ServiceItems.Remove(serviceItem2);
context.SaveChanges();
}
サービス15をserviceItem23および37にリンクする結合テーブルの2つの関係行が削除されます。
または、Attach
を呼び出す代わりに、データベースから既存のエンティティをロードできます。それも機能します:
var service = context.Services.Single(s => s.ServiceID == 15);
また、既存のServiceItem
sについても同じです。