21

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データベーステーブルは更新されません。どんなアドバイスでも大歓迎です。

-ありがとう

4

1 に答える 1

68

何も起こらないことが予想されます。

変更または追加したいのは、エンティティとの間の関係です。ただし、エンティティの状態を に設定して関係を操作することはできません。これは、スカラーおよび複雑なプロパティのみを更新し、ナビゲーション プロパティ (= 関係) は更新しません。(たとえば、エンティティの状態を に設定すると、 や などが変更済みとしてマークされ、これらのプロパティがデータベースに保存されるようになります。ただし、 の内容は気にしません。)ServiceServiceItemModifiedServiceModifiedService.TitleService.DescriptionService.ServiceItems

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

多対多の関連付けの関係を直接操作する方法はありますが、その方法はObjectContextRelationshipManager私の意見では、かなり高度でトリッキーです。

多対多の関連付けに関係エントリを追加および削除する通常の簡単な方法は、エンティティがコンテキストに関連付けられている間に、コレクションに項目を追加したり、コレクションから項目を削除したりすることです。EF の変更追跡メカニズムは、行った変更を認識し、呼び出し時に適切な INSERT、UPDATE、および DELETE ステートメントを生成しますSaveChanges

正確な手順は、新しいエンティティとして保存するか、ServiceまたはServiceItem既存のエンティティ間の関係のみを追加するかによって異なります。以下にいくつかの例を示します。

  • serviceを INSERT する必要があり、すべてserviceItemの を INSERT する必要があり、エンティティ間の関係も結合テーブルに INSERT する必要があります。

    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 がグラフ内の他のすべてのエンティティがコンテキストに関連付けられていないことを認識し、それらをデータベースに INSERT する必要があると想定するためです。

  • serviceは既に存在するため、INSERT するべきではありません。すべてserviceItemの を INSERT する必要があり、エンティティ間の関係も結合テーブルに INSERT する必要があります。

    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アタッチされていることを認識しますが、他のエンティティは認識しません。INSERT はservice発生しませんが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の s についても同様です。

于 2012-12-31T13:59:28.277 に答える