多对多的关系不能保存

时间:2012-12-31 07:07:58

标签: entity-framework many-to-many

我有两个实体,我在EF 5 Code First中创建了一个相当标准的多对多关系。这些是Service和ServiceItem。 Service实体包含ServiceItems的集合,ServiceItem包含Services集合。我可以创建,更改和保存数据到任一实体基本属性,没有任何问题。当我尝试将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的Service项的代码。我已经仔细验证了ServiceItems是否在正确的集合中。

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

dbService对象似乎没有受到任何影响。 ServiceItem仍在正确的集合中,但不对ServiceItemService数据库表进行更新。任何建议都会非常受欢迎。

-Thanks

1 个答案:

答案 0 :(得分:64)

预计没有任何事情发生。

您要更改或添加的内容是实体ServiceServiceItem之间的关系。但是,您无法通过将实体的状态设置为Modified来操纵关系。这仅更新标量和复杂属性,但不更新导航属性(=关系)。 (例如,将Service实体的状态设置为Modified会将Service.TitleService.Description等标记为已修改,并确保将这些属性保存到数据库中。它并不关心Service.ServiceItems的内容。)

通过将状态设置为Modified,您可以更改关系的唯一例外是外键关联。这些是在模型实体中公开的外键属性的关联,它们只能出现一对多或一对一关联。多对多关系始终是独立关联,这意味着它们永远不会在实体中拥有外键属性。 (因为FK位于连接表中,但连接表不是实体,而是从模型类中“隐藏”。)

有一种方法可以直接操作多对多关联的关系,但它需要转到ObjectContext及其RelationshipManager,这在我看来是非常先进和棘手的

在多对多关联中添加和删除关系条目的常用和直接方法是在附加实体时向集合添加项目和从中删除项目在上下文中。当您调用SaveChanges时,EF的更改跟踪机制将识别您所做的更改并生成相应的INSERT,UPDATE和DELETE语句。

确切的过程取决于您是否还要将Service和/或ServiceItem保存为新实体,或者只想添加现有实体之间的关系。以下是一些例子:

  • service应该被INSERT,所有serviceItem都应该被插入,实体之间的关系也应该被插入到连接表中:

    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已经存在且不应该被INSERT,所有serviceItem都应该被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已附加但其他实体未附加。 service没有INSERT,但serviceItem1/2将与关系条目一起插入。

  • 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与serviceItem 23和37链接的两个关系行将被删除。

另外,您可以从数据库加载现有实体,而不是调用Attach。它也会起作用:

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

现有的ServiceItem也一样。