Причина, по которой [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,
});
Но вы уверены, что такого отношения уже нет? Вы должны будете получить это сначала. И уверены ли вы, что кто-то еще не добавил связь между вашей выборкой и добавлением?
Видите ли, столько проблем, просто не стоит самостоятельно разбирать распределительную таблицу!