Ho una struttura di tabella come questa:
ID Code Value
1 2 text1
2 3 text3
2 4 text4
Qui ID e codice formano la chiave primaria composta, ovvero nella tabella sopra non è possibile avere un'altra riga con ID = 2 e Codice = 4.
Ora stiamo usando l'entity framework core e ho una classe chiamata Branch (che rappresenta la chiave composta) e che assomiglia a questo:
public class Branch
{
public int ID {get; set;}
public int Code {get; set;}
}
In secondo luogo ho anche una List<Branch>
. Ora voglio fare due cose:
Per prima cosa effettua una chiamata al database e ottieni l'oggetto completo (ID, Codice, Valore) per l'intero elenco di Branch.
Dopodiché, cambierò il "valore" per ogni oggetto.
Quindi voglio effettuare una chiamata al database e salvare il 'Valore' aggiornato per ogni riga.
Attualmente sto facendo questo in un ciclo quindi è inefficiente.
foreach(var x in listOfBranch)
{
var record = context.Table.Find(x.ID, x.Code);
record.Value = "new value";
context.SaveChanges();
}
Come possiamo farlo in una sola chiamata? Grazie.
Ora voglio fare due cose:
Per prima cosa effettua una chiamata al database e ottieni l'oggetto completo (ID, Codice, Valore) per l'intero elenco di Branch.
Dopodiché, cambierò il "valore" per ogni oggetto.
Quindi voglio effettuare una chiamata al database e salvare il 'Valore' aggiornato per ogni riga.
La seconda parte è semplice: basta spostare la chiamata di SaveChanges()
all'esterno del loop.
La prima parte è complicata. Al momento della scrittura (EF Core 2.0.2), Contains
può essere utilizzato solo con una singola proprietà e si unisce a raccolte di memoria, query basate su Concat
o filtri come
.Where(e => listOfBranch.Any(b => b.ID == e.ID && b.Code == e.Code))
tutti questi non sono tradotti in SQL e sono valutati localmente.
L'unico modo per ottenere il risultato desiderato con una singola query di database è creare dinamicamente un criterio di filtro come questo:
.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)
)
È possibile creare il predicato precedente utilizzando i metodi della classe Expression
come segue (è sufficiente sostituire Record
con il tipo di entità):
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);
e l'uso:
foreach (var record in context.Table.Where(predicate))
{
record.Value = "new value";
}
context.SaveChanges();