多对多的关系不能保存

dro*_*son 20 many-to-many entity-framework

我有两个实体,我在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; }
}
Run Code Online (Sandbox Code Playgroud)

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; }

}
Run Code Online (Sandbox Code Playgroud)

这是我在尝试调试此问题时所做的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");
           });
    }
Run Code Online (Sandbox Code Playgroud)

这是我用来保存服务项的代码,该服务项包含Service.ServiceItems集合中的2-3个ServiceItems.我已经仔细验证了ServiceItems是否在正确的集合中.

                db.Entry(dbService).State = EntityState.Modified;
                db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

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

-谢谢

Sla*_*uma 64

预计没有任何事情发生.

您想要更改或添加的是实体和实体之间的关系.但是你不能通过设置实体的状态来操纵关系.这仅更新标量和复杂属性,但不更新导航属性(=关系).(例如设置状态实体将迎来和等改性,并确保这些属性保存到数据库中.不过,这并不关心的内容.)ServiceServiceItemModifiedServiceModifiedService.TitleService.DescriptionService.ServiceItems

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

有一种方法可以直接操纵关系的许多-to-many关联,但它需要深入到ObjectContextRelationshipManager是-在我看来-相当先进和复杂.

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

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

  • service应该是INSERTed,所有serviceItems都应该被INSERTed,并且实体之间的关系也应该被插入到连接表中:

    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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    添加service对象图的"根" 就足够了,因为EF会识别出图中的所有其他实体都没有附加到上下文,并假设它们必须被插入到数据库中.

  • service已经存在且不应该被INSERT,所有serviceItems都应该被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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    EF在此处(当SaveChanges被调用时)识别service附加但其他实体不是.不会service发生INSERT,但serviceItem1/2会将其与关系条目一起插入.

  • service已经存在且不应该被INSERT,所有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();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 为了完整性:如何删除现有实体之间的关系?

    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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    将删除将服务15与serviceItem 23和37链接的连接表中的两个关系行.

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

var service = context.Services.Single(s => s.ServiceID == 15);
Run Code Online (Sandbox Code Playgroud)

现有的ServiceItems也一样.

  • 感谢Slauma提供了如此详细的答案.你真的帮我理解了一些不清楚的东西,它解决了我的问题. (6认同)
  • Slauma!你不知道我现在有多爱你.我一直在网上搜索我的问题的答案,你这么惊人地帮我解决了!我在我的应用程序中处于一个有点不连贯的场景中工作并且阅读你的答案让我意识到我需要在使用相同的上下文时立即完成所有工作.谢谢谢谢谢谢谢谢! (3认同)