Как я могу эффективно установить коллекцию объектов «многие ко многим» в Entity Framework?

#entity-framework-4 #linq-to-entities #many-to-many #entitycollection #addrange

#entity-framework-4 #linq-to-entities #многие ко многим #коллекция сущностей #добавить диапазон

Вопрос:

Когда Entity Framework генерирует ObjectContext для двух таблиц базы данных (скажем, Table1 и Table2), связанных с таблицей отношений «многие ко многим», он не создает объект для таблицы внешних ссылок, вместо этого выбирая свойства коллекции на обоих концах связи. Итак, у вас есть таблица 1 EntityCollection<Table2> Table2s и у вас есть таблица 2 EntityCollection<Table2> Table1s . В большинстве случаев это на самом деле довольно здорово…

Однако в этом сценарии у меня есть список целых чисел, которые представляют идентификаторы базы данных строк Table2, которые должны быть в Table1.Коллекция Table2s.

Я не вижу никакого способа просто установить эту коллекцию, используя ключи сущностей, поэтому я застрял, выбирая их в ObjectContext, что уже требует тонны работы без причины. Я позволяю себе надеяться, что LINQ-to-Entities разумно отложит выполнение и выполнит все это на сервере SQL, как мне хотелось бы (хотя мое Where использует Contains, которое может быть или не быть правильно переведено в IN () в SQL). Итак, я могу зайти так далеко, как:

 table1instance.Table2s.Clear();
var table2sToInclude = context.Table2s.Where(
  t => 
  listOfTable2DatabaseIds.Contains(t.Id));
  

Но там нет EntityCollection<T>.AddRange(IEnumerable<T>) или чего-либо еще, и, конечно, нет IEnumerable<T>.ToEntityCollection<T>() метода расширения, поэтому я не знаю, что делать с этими результатами на данный момент. Все, что я могу сделать, это

 foreach (var table2 in table2sToInclude)
{
  table1instance.Table2s.Add(table2);
}
  

это кажется нелепым, и я знаю, что это приведет к большому количеству ненужных вычислений.

Есть ли «правильный» или, возможно, «менее отстойный» способ сделать это?

Ответ №1:

Нет, EF не будет откладывать выполнение любого запроса. Нет ничего похожего на insert из select. Linq-to-entities — это просто язык запросов, и ответственность за выполнение запроса заключается. Это строго отделено от функциональности сохранения, предлагаемой самим EF.

Если вы хотите создать отношения между существующим элементом из table1 и выходящими элементами из table2, вы можете использовать код, подобный этому:

 using (var ctx = new YourContext())
{
    var table1 = new Table1 { Id = 123 };
    ctx.Table1s.Attach(table1);

    foreach (var table2 in table2sToInclude.Select(id => new Table2 { Id = id }))
    {
        ctx.Table2s.Attach(table2);
        order.Table2s.Add(table2);
    }
    ctx.SaveChanges();
}
  

Этот код создает связь между элементом Table1 с идентификатором 123 и всеми элементами Table2 из table2sToInclude без загрузки какой-либо отдельной записи из базы данных.

Что делает добавление записей одну за другой «хромым»? Вы понимаете, в чем преимущество AddRange ? AddRange в типичной коллекции увеличивается емкость внутреннего массива и просто копируются элементы в расширенный массив. EntityCollection это не типичный массив, и он должен обрабатывать каждую добавленную сущность. Таким образом, даже если их будет несколько AddRange , он будет выполнять внутреннюю итерацию элементов и обрабатывать их по одному.

Комментарии:

1. Речь шла не о том, чтобы выбирать один за другим по сравнению с AddRange , за исключением наивной надежды, что вызов AddRange в этой ситуации (если бы он существовал) не вызвал бы операторов SELECT, из которых мне не нужна никакая информация (поскольку у меня уже есть идентификаторы EntityKey), Подход создания экземпляров новых объектов Table2 и вызова Attach должен сработать. Спасибо