Автоматическое добавление / удаление строк в объединенной таблице - PullRequest
1 голос
/ 11 апреля 2019

Я заметил, что в отношениях «многие ко многим» объединенная таблица поддерживается ef.т.е.

Center 
ID     name
1      C1
2      C2

Facility
ID     Name
1      F1
2      F2

Center_Facitliy
CenterID    FacilityID
1             1
1             2
2             1
2             2

Если я сделаю

Center = GetCenterById(2);Indcluds Center_Facilities collection
Center.Center_Facilities = new List<Center_Facility> { 
                               new Center_Facility { FacilityID = 3 }, 
                               new Center_Facility { FacilityID = 1}    
                            }

это обновление таблицы Center_Facility до

Center_Facitliy
CenterID    FacilityID
1             1
1             2
2             1
2             3

. Он автоматически удаляет 2-2 и добавляет 2-3.Может ли кто-нибудь объяснить, почему и как это происходит?

Этого не происходит с нижеприведенным

CenterEmployee
ID   Name     Phone    CenterID
1    E1       123       1
2    E2       123       1
3    E3       123       2

(сотрудник может работать только в одном центре)

Center = GetCenterById(1); //Indcluds CenterEmployee collection
Center.CenterEmployee = new List<CenterEmployee> { 
                               new CenterEmployee { Name=E5, Phone=123 }, 
                               new CenterEmployee { Name=E4, phone=123 }    
                            }
Result
CenterEmployee
ID   Name     Phone    CenterID
1    E1       123       null
2    E2       123       null
3    E3       123       2
4    E4       123       1
5    E5       123       1

Он обновил отношение FK, но не удалил старыйзапись (1,2).

Спасибо

Ответы [ 2 ]

2 голосов
/ 11 апреля 2019

Причина, по которой [2, 2] и [2, 3] удалены, потому что вы только что сообщили dbContext, что Центр [2] имеет только центры с идентификаторами [1] и [3].

Вы правы, система управления реляционными базами данных реализует отношение "многие ко многим", используя отдельную таблицу: таблицу соединений. Обычно таблица соединений имеет только идентификаторы подключенных элементов. Поскольку комбинация [CenterId, FacilityId] уникальна, эта комбинация обычно используется в качестве первичного ключа, что делает его очень быстрым для запроса.

Если вы сначала проектируете отношение «многие ко многим» в коде структуры сущностей, вам не нужно упоминать таблицу соединений:

class Center
{
    public int Id {get; set;}
    ...

    // every Center offers zero or more facilities (many-to-many)
    public virtual ICollection<Factility> Facilities {get; set;}
}
class Facility
{
    public int Id {get; set;}
    ...

    // every Facility is offered at zero or more Centers (many-to-many)
    public virtual ICollection<Center> Centers {get; set;}
}
class MyDbContext : DbContext
{
    public DbSet<Center> Centers {get; set;}
    public DbSet<Facility> Facilities {get; set;}
}

Обратите внимание, что таблица соединений не упоминается!

В структуре сущностей столбцы таблиц представлены не виртуальными свойствами. Виртуальные свойства представляют отношения между таблицами.

Из-за двусторонней виртуальной ICollection, структура сущностей может обнаружить, что вы спроектировали отношение «многие ко многим», и автоматически создаст для вас подходящую таблицу соединений, без атрибутов или свободного API.

Но как выполнить запрос без таблицы соединений?

Не используйте соединительную таблицу, вместо этого используйте ICollections:

Дайте мне (несколько свойств) британские центры с (несколько свойств) их устаревшими объектами:

var result = dbContext.Centers
    .Where(center => center.CountryCode == "UK")
    .Select(center => new
    {
        // select only the properties that you plan to use
        Id = center.Id,
        Name = center.Name,
        ...

        ObsoleteFacilities = center.Facilities
          .Where(facility => facility.Obsolete)
          .Select(facility => new
          {
              // again, only the properties you plan to use
              Id = facility.Id,
              ...
          })
          .ToList(),
     });

Платформа сущностей знает ваше «многие ко многим» и достаточно умна, чтобы понять, что для этого необходимо (групповое) соединение с таблицей соединений.

Теперь, если вы выбрали Center [2] и Facility [3], их легко соединить, не беспокоясь о том, существует ли уже связь между ними:

var center2 = dbContext.Centers.Find(2);
var facility3 = dbContext.Facilities.Find(3);
// TODO: check if really found

// connect them:
center2.Facilities.Add(facility3);

// add a new facility:
center.Facilities.Add(new Facility() {...});
dbContext.SaveChanges();

В качестве альтернативы вы могли бы добавить центр к объекту:

facility3.Centers.Add(center2);
dbContext.Facilities.Add(new Facility()
{
    ...
    Centers = new Center[] {center2},
});

Хорошая вещь об этом, кроме того, что это очень интуитивно понятно, это то, что вам не нужно беспокоиться о том, существует ли уже связь между центром 2 и объектом 3 или нет.

Недостатком является то, что для этого вам придется получить весь центр и объект. Обычно вы будете запрашивать данные гораздо чаще, чем добавлять и обновлять, и изменение отношений обычно выполняется после (относительно медленного) взаимодействия с пользователем, поэтому я сомневаюсь, что это когда-нибудь станет проблемой.

Но если вы действительно хотите добавить отношение без необходимости сначала извлекать центр и объект, вы можете добавить DbSet, содержащий таблицу соединений

public DbSet<CenterFacility> CenterFacilities {get; set;}

Добавить отношение:

dbContext.CenterFunctions.Add(new CenterFacility()
{
    CenterId = 2,
    FacilityId = 3,
});

Но вы уверены, что такого отношения уже нет? Вы должны будете получить это сначала. И уверены ли вы, что кто-то еще не добавил связь между вашей выборкой и добавлением?

Видите ли, столько проблем, просто не стоит самостоятельно разбирать распределительную таблицу!

2 голосов
/ 11 апреля 2019

Когда вы обновляете Center.Center_Facilities следующим образом:

Center.Center_Facilities = new List<Center_Facility> { 
                               new Center_Facility { FacilityID = 3 }, 
                               new Center_Facility { FacilityID = 1}    
                            }

Это означает, что вы заменяете существующую коллекцию Center_Facilities (которая была загружена в контекст) в Center новой Center_Facilities коллекция, и поэтому предыдущая коллекция удаляется, а новая коллекция добавляется.Это ожидаемое поведение Entity Framework, в противном случае, если ваша новая коллекция содержит набор Center_Facility, который уже существует в базе данных, тогда она выдаст исключение дублирующегося ключа.

Более того, теперь подумайте, что вы не хотите заменятьно чтобы добавить к существующей коллекции Center_Facilities, вам нужно сделать следующее:

Center.Center_Facilities.AddRange(new List<Center_Facility> { 
                                   new Center_Facility { FacilityID = 3 }, 
                                   new Center_Facility { FacilityID = 1}    
                                })

И в этом случае вы должны убедиться, что новая добавленная коллекция не содержит никакого набора Center_Facilityэто уже существует в базе данных.

Надеюсь, теперь вам ясно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...