Подтвердить что ты не робот

От многих до многих отношений не сохраняется

У меня есть два объекта с довольно стандартным отношением Many to Many, которое я создал в EF 5 Code First. Это Service и ServiceItem. Объект службы содержит набор ServiceItems, а 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");
           });
    }

Вот код, который я использую для сохранения элемента сервиса, который включает 2-3 ServiceItems в коллекции Service.ServiceItems. Я тщательно проверил, что ServiceItems находятся в соответствующей коллекции.

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

Объект dbService никак не влияет. Службы ServiceItems по-прежнему находятся в правильной коллекции, но обновление не производится в таблице базы данных ServiceItemService. Любые советы были бы очень желанными.

-Спасибо

4b9b3361

Ответ 1

Ожидается, что ничего не произойдет.

То, что вы хотите изменить или добавить, - это связь между объектами Service и ServiceItem. Но вы не можете манипулировать отношениями, установив состояние объекта в Modified. Это обновляет только скалярные и сложные свойства, но не обладает свойствами навигации (= отношения). (Например, настройка состояния объекта Service на Modified будет отмечена как Service.Title и Service.Description и т.д. Как измененная, и убедитесь, что эти свойства сохранены в базе данных, но это не заботится о содержимом of Service.ServiceItems.)

Единственное исключение, в котором вы можете изменить отношения, установив состояние в Modified, - это ассоциации внешних ключей. Это ассоциации, которые имеют свойства внешнего ключа, выставленные в вашей модели, и они могут встречаться только для ассоциаций "один ко многим" или "один к одному". Отношения "многие ко многим" всегда являются независимыми ассоциациями, что означает, что они никогда не могут иметь свойство внешнего ключа в сущности. (Поскольку FK находятся в таблице соединений, но таблица соединений не является сущностью и "скрыта" от ваших классов моделей.)

Существует способ прямого отношения к отношениям для ассоциации "многие-ко-многим", но для этого требуется перейти к ObjectContext и его RelationshipManager, который, на мой взгляд, довольно продвинутый и сложный.

Обычный и прямой способ добавления и удаления записей отношений в/из ассоциации "многие-ко-многим" - это просто добавление элементов и удаление элементов из коллекций, в то время как сущности привязаны к контексту. Механизм отслеживания изменений EF распознает сделанные вами изменения и сгенерирует соответствующие инструкции INSERT, UPDATE и DELETE при вызове SaveChanges.

Точная процедура зависит от того, хотите ли вы сохранить Service и/или ServiceItem в качестве новых объектов или хотите только добавить отношения между существующими объектами. Вот несколько примеров:

  • Service должен быть INSERTed, все ServiceItem должны быть INSERTED, а отношения между сущностями также должны быть 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();
    }
    

    Добавление "root" Service графа объекта достаточно, потому что EF распознает, что все другие объекты в графе не привязаны к контексту и предполагают, что они должны быть INSERTed в базу данных.

  • Service уже существует и НЕ должен быть INSERTed, все ServiceItem должны быть INSERTed, и отношения между сущностями также должны быть INSERTed в таблице соединений:

    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 будет INSERTed вместе с записями отношений.

  • Service уже существует и НЕ должен быть INSERTed, все ServiceItem уже существуют и НЕ должны быть INSERTed, но отношения между объектами должны быть INSERTed в таблице соединений:

    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 s.