许多关系不能保存 [英] Many to Many Relationships not saving

查看:90
本文介绍了许多关系不能保存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个实体,我在EF 5 Code First中创建了一个相当标准的多对多关系。这些是Service和ServiceItem。 Service实体包含一个ServiceItem集合,ServiceItem包含一组Services。我可以创建,更改和保存数据到任何一个实体的基本属性没有问题。当我尝试将ServiceItem添加到服务或服务到ServiceItem它似乎工作,但没有任何东西被保存。我已经验证了创建了所有正确的数据库表,包括带有十字键的ServiceItemService表。当我添加项目时,数据库ServiceItemService表不会得到任何条目。没有错误,一切似乎都能完美地工作。



我有点困惑,可以使用一些帮助。以下是课程。



Service class;

  public class服务
{
//默认构造函数
public Service()
{
//默认值
IsActive = true;
ServicePeriod = ServicePeriodType.Monthly;
ServicePeriodDays = 0;

ServiceItems = new Collection< ServiceItem>();
}

public int ServiceID {get;组; }
public string标题{get;组; }
public string描述{get;组; }
public ICollection< ServiceItem> ServiceItems {get;组; }
public string TermsOfService {get;组; }
public ServicePeriodType ServicePeriod {get;组; }
public int ServicePeriodDays {get;组; }
public bool IsActive {get;组; }
}

ServiceItem类;

  public class ServiceItem 
{
public ServiceItem()
{
IsActive = true;
}

public int ServiceItemID {get;组; }
public string标题{get;组; }
public string描述{get;组; }
public ICollection< Service>服务{get;组; }
public string UserRole {get;组; }
public bool IsActive {get;组; }

}

这是我在尝试调试时所做的Fluent映射这个问题。在添加此映射之前和之后,同样的问题发生。

  public DbSet< Service>服务{get;组; } 
public DbSet< ServiceItem> ServiceItems {get;组;

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

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

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

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



- 谢谢

解决方案



您想要更改或添加的是实体之间的关系 Service ServiceItem 。但您不能通过将实体的状态设置为修改来操纵关系。这只会更新标量和复杂属性,但没有导航属性(=关系)。 (例如将服务实体的状态设置为修改将标记 Service.Title Service.Description 等进行修改,并确保将这些属性保存到数据库中,但不关心 Service.ServiceItems 。)



唯一的例外,您可以通过设置更改关系状态为修改外键关联。这些是在模型实体中暴露的外键属性的关联,它们只能发生一对多关联或一对一关联。多对多关系始终是独立关联,这意味着它们在实体中永远不会有外键属性。 (因为FKs在连接表中,但是连接表不是一个实体,而是模型类中的隐藏)。



有一种方法来直接操作关系为多对多关联,但它需要下到 ObjectContext 及其 RelationshipManager 这是 - 在我看来,这是相当先进和棘手的。



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



确切的程序取决于您是否还要将服务和/或 ServiceItem 保存为新实体或如果您只想添加现有实体之间的关系。以下是一些示例:




  • 服务应该被INSERT,所有应该插入 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服务对象图是足够的,因为EF会认识到图中的所有其他实体都不会附加到上下文中,并假设它们必须被插入到数据库中。


  • 已经存在
  • 服务,不应该被插入,所有 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 被调用)服务被附加,但其他实体不是。 服务的I​​NSERT不会与关系条目一起插入 serviceItem1 / 2

    / li>
  • 服务已经存在,不应该被插入,所有 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)//加载现有项目
    .Single(s => s.ServiceID == 15);

    var serviceItem1 = service.ServiceItems
    .Single(s => s.ServiceItemID == 23); //在内存中查询,没有DB查询
    var serviceItem2 = service.ServiceItems
    .Single(s => s.ServiceItemID == 37); //在内存中查询,没有DB查询

    service.ServiceItems.Remove(serviceItem1);
    service.ServiceItems.Remove(serviceItem2);

    context.SaveChanges();
    }

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




替代而不是调用附加您可以从数据库加载现有实体。它也可以工作:

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

与现有的 ServiceItem 相同。


I have two entities with a fairly standard Many to Many relationship that I created in EF 5 Code First. These are Service and ServiceItem. The Service entity contains a collection of ServiceItems and the ServiceItem contains a collection of Services. I can create, change and save data to either of the entities basic properties with no problems. When I try to add a ServiceItem to a Service or a Service to a ServiceItem it seems to work, but nothing is saved. I have verified that all the proper database tables are created, including a ServiceItemService table with the cross keys. The database ServiceItemService table doesn't get any entry when I add the items. There is no error and everything else seems to work perfectly.

I am a bit stumped and could use some help. Below are the classes.

The Service class;

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

The ServiceItem class;

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

}

This is the Fluent mapping I did while trying to debug this issue. The same problem happened before and after adding this mapping.

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

Here is the code I use to save the Service item that includes 2-3 ServiceItems in the Service.ServiceItems collection. I have carefully verified that the ServiceItems were in the proper collection.

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

The dbService object doesn't seem to get affected in any way. The ServiceItems are still in the proper collection, but no update are made to the ServiceItemService database table. Any advice would be very welcome.

-Thanks

解决方案

It is expected that nothing happens.

What you want to change or add is a relationship between the entities Service and ServiceItem. But you cannot manipulate relationships by setting the state of an entity to Modified. This only updates scalar and complex properties but no navigation properties (= relationships). (For example setting the state of a Service entity to Modified will mark Service.Title and Service.Description, etc. as modified and ensure that those properties are saved to the database. But it doesn't care about the content of Service.ServiceItems.)

The only exception where you can change a relationship by setting the state to Modified are Foreign Key Associations. These are associations that have foreign key properties exposed in your model entity and they can only occur for one-to-many or one-to-one associations. Many-to-many relationships are always Independent Associations which means they can never have a foreign key property in an entity. (Because the FKs are in the join table, but the join table is not an entity and "hidden" from your model classes.)

There is a way to directly manipulate relationships for a many-to-many association but it requires to go down to the ObjectContext and its RelationshipManager which is - in my opinion - pretty advanced and tricky.

The usual and straight-forward way to add and remove relationship entries to/from a many-to-many association is by just adding items to and removing items from the collections while the entities are attached to the context. EF's change tracking mechanism will recognize the changes you have done and generate the appropriate INSERT, UPDATE and DELETE statements when you call SaveChanges.

The exact procedure depends on if you also want to save Service and/or ServiceItem as new entities or if you only want to add relationships between existing entities. Here are a few examples:

  • service should be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:

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

    Adding the "root" service of the object graph is enough because EF will recognize that all other entities in the graph are not attached to the context and assume that they have to be INSERTed into the database.

  • service already exists and should NOT be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:

    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 recognizes here (when SaveChanges is called) that service is attached but the other entities are not. No INSERT for service happens but the serviceItem1/2 will be INSERTed together with the relationship entries.

  • service already exists and should NOT be INSERTed, all serviceItems already exist and should NOT be INSERTed, but the relationships between the entities should be INSERTed into the join table:

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

  • For completeness: How to remove relationships between existing entities?

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

    The two relationship rows in the join table that link service 15 with serviceItem 23 and 37 will be deleted.

Alternativly instead of calling Attach you can load the existing entities from the database. It will work as well:

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

And the same for existing ServiceItems.

这篇关于许多关系不能保存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆