Entity Framework Core: чтение и обновление нескольких строк - составной первичный ключ - PullRequest
0 голосов
/ 10 мая 2018

У меня есть такая структура таблицы:

ID Code  Value
1  2     text1
2  3     text3
2  4     text4

Здесь ID и код формируют составной первичный ключ, то есть в приведенной выше таблице не может быть другой строки с ID = 2 и кодом = 4.

Теперь мы используем ядро ​​Entity Framework, и у меня есть класс с именем Branch (представляющий составной ключ), который выглядит следующим образом:

public class Branch
{
 public int ID {get; set;}
 public int Code {get; set;}
}

Во-вторых, у меня тоже есть List<Branch>. Теперь я хочу сделать две вещи:

Сначала выполните один вызов базы данных и получите полный объект (ID, код, значение) для всего списка Branch.

После этого я буду менять «Значение» для каждого объекта.

Затем я хочу сделать один вызов базы данных и сохранить обновленное «Значение» для каждой строки.

В настоящее время я делаю это в цикле, так что это неэффективно.

foreach(var x in listOfBranch)
{
  var record = context.Table.Find(x.ID, x.Code);
  record.Value = "new value";
  context.SaveChanges();
}

Как мы можем сделать это за один звонок? Спасибо.

1 Ответ

0 голосов
/ 11 мая 2018

Теперь я хочу сделать две вещи:

Сначала выполните один вызов базы данных и получите полный объект (ID, код, значение) для всего списка Branch.

После этого я буду менять «Значение» для каждого объекта.

Затем я хочу сделать один вызов базы данных и сохранить обновленное «Значение» для каждой строки.

Вторая часть проста - просто переместите SaveChanges() вызов за пределы цикла.

Первая часть хитрая. На момент написания (EF Core 2.0.2) Contains можно использовать только с одним свойством и объединять в коллекции памяти, запросы на основе Concat или фильтры типа

.Where(e => listOfBranch.Any(b => b.ID == e.ID && b.Code == e.Code))

все они не переведены в SQL и оцениваются локально.

Единственный способ получить желаемый результат с помощью одного запроса к базе данных - это динамически построить критерии фильтра, подобные этому:

.Where(e =>
    (e.ID == listOfBranch[0].ID && e.Code == listOfBranch[0].Code)
    ||
    (e.ID == listOfBranch[1].ID && e.Code == listOfBranch[1].Code)
    // ...
    ||
    (e.ID == listOfBranch[N-1].ID && e.Code == listOfBranch[N-1].Code)
)

Вы можете построить вышеупомянутый предикат, используя Expression методы класса следующим образом (просто замените Record типом сущности):

var parameter = Expression.Parameter(typeof(Record));

var body = listOfBranch
    .Select(b => Expression.AndAlso(
        Expression.Equal(Expression.Property(parameter, "ID"), Expression.Constant(b.ID)),
        Expression.Equal(Expression.Property(parameter, "Code"), Expression.Constant(b.Code))))
    .Aggregate(Expression.OrElse);

var predicate = Expression.Lambda<Func<Record, bool>>(body, parameter);

и использование:

foreach (var record in context.Table.Where(predicate))
{
    record.Value = "new value";
}
context.SaveChanges();
...