2012-12-31 13 views
18

私はEF 5コードファーストで作成した多対多リレーションシップを持つ2つのエンティティを持っています。これらはサービスとServiceItemです。サービスエンティティにはServiceItemのコレクションが含まれ、ServiceItemにはサービスのコレクションが含まれます。どちらのエンティティにも問題なくデータを作成、変更、保存することができます。 ServiceItemを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を含むサービスアイテムを保存するためのコードを示します。 ServiceItemが適切なコレクションに入っていることを慎重に確認しました。

   db.Entry(dbService).State = EntityState.Modified; 
       db.SaveChanges(); 

dbServiceオブジェクトはどのような形でも影響を受けていないようです。 ServiceItemはまだ適切なコレクションにありますが、ServiceItemServiceデータベーステーブルは更新されません。どんな助言も大歓迎です。

-お問い合わせ

答えて

61

何も起こりません。

変更または追加する対象は、ServiceServiceItemの間のの関係です。しかし、エンティティの状態をModifiedに設定することで、関係を操作することはできません。これは、スカラープロパティと複合プロパティを更新するだけですが、ナビゲーションプロパティ(=関係)は更新しません。 (例えば、改変さ等Service.TitleService.Descriptionをマークし、これらのプロパティは、データベースに保存されていることを保証するModifiedServiceエンティティの状態を設定する。しかし、それはService.ServiceItemsの内容を気にしない。)

関係をModifiedに設定してを変更できる唯一の例外は、外部キーの関連付けです。これらは、モデルエンティティで外部キーのプロパティが公開されているアソシエーションで、1対1または1対1のアソシエーションに対してのみ発生します。多対多の関係は常に独立したアソシエンスです。つまり、エンティティ内に外部キーのプロパティを持つことはできません。 (FKは結合テーブルにありますが、結合テーブルはエンティティではなく、モデルクラスから「隠されている」ため)

多対多関連のリレーションシップを直接操作する方法はありますが、 ObjectContextとそのRelationshipManagerに行く必要があります - これは私の意見ではかなり進歩したしトリッキーです。

多対多の関連付けに関係エントリを追加および削除する通常の方法は、コレクションにアイテムを追加したり削除したりすることです。エンティティはコンテキストにを添付します。 SaveChangesを呼び出すと、EFの変更追跡メカニズムによって変更が認識され、適切な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(); 
    } 
    

    オブジェクトグラフの「root」serviceで十分です。これは、グラフ内の他のすべてのエンティティがコンテキストにアタッチされていないことをEFが認識し、データベースにINSERTする必要があると想定するためです。

  • serviceがすでに存在して挿入してはいけません、すべてのserviceItem sが挿入されなければならないとエンティティ間の関係も同様に参加するテーブルに挿入する必要があります。SaveChangesがあるとき

    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は(ここで認識します)serviceが添付されていますが、他のエンティティは添付されていません。 serviceのINSERTは発生しませんが、serviceItem1/2はリレーションシップエントリとともにINSERTされます。

  • serviceはすでに存在していると、すべてのserviceItem sがすでに存在して挿入するべきではなく、挿入されるべきではなく、エンティティ間の関係は、結合テーブルに挿入する必要があります:完全性については

    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とserviceItem 23と37をリンクする2つのリレーションシップ行が削除されます。

代わりに、Attachを呼び出す代わりに、データベースから既存のエンティティを読み込むことができます。それはうまくいくでしょう:

var service = context.Services.Single(s => s.ServiceID == 15); 

そして、既存のServiceItemと同じです。

+6

このような詳細な回答をいただきありがとうございます。あなたは本当に私が明確ではないいくつかのことを理解するのを助け、私の問題を解決しました。 – drobertson

+3

Slauma!あなたは今あなたをどれだけ愛しているのか分かりません。私は驚くほど私が解決するのを助けた私の問題への答えのためにインターネットを精練しています!私はアプリ内でやや切り離されたシナリオで作業しており、あなたの答えを読んで、私は即座にすべての仕事をしたのと同じコンテキストを使用してすべてを行う必要があることを認識しました。ありがとう、ありがとう、ありがとう、ありがとう! –

+0

これはすばらしい答えです。EFの基礎を理解するのに役立ちました。しかし、これをMVC Webアプリケーションのリポジトリパターンにどのように適用すればよいでしょうか? – BeeTee2

関連する問題