Sto utilizzando Entity Framework Core per recuperare entità già memorizzate nel database, ma a seconda di come lo faccio, a volte vengono recuperate nello stato "Staccato", anche quando non sto utilizzando AsNoTracking
.
Queste sono le classi utilizzate per modellare il database:
class AppDbContext : DbContext
{
public DbSet<Thing> Thing { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("...");
}
}
class Thing
{
public int ThingId { get; set; }
public string Name { get; set; }
}
Di seguito è riportata una classe utilizzata per riprodurre lo scenario in cui le entità vengono recuperate in uno stato disconnesso:
class Wrapper
{
public Thing Thing { get; set; }
public Wrapper(Thing t)
{
Thing = t;
}
}
Il programma principale quindi procede come segue:
foreach (var wrapper in context.Thing.Select(a => new Wrapper(a)))
{
Console.WriteLine(context.Entry(wrapper.Thing).State);
}
foreach (var thing in context.Thing.Select(a => a))
{
Console.WriteLine(context.Entry(thing).State);
}
Supponendo che ci siano tre righe nella tabella Thing
, l'output diventa il seguente:
Detached
Detached
Detached
Unchanged
Unchanged
Unchanged
In altre parole, le entità vengono separate se recuperate e quindi passate nel costruttore Wrapper
ma tracciate (nello stato "Invariato") se semplicemente recuperate regolarmente.
Sono a conoscenza del fatto che le entità già mantenute nel database devono sempre essere recuperate in uno stato tracciato a meno che non vengano richiamate esplicitamente con AsNoTracking
, quindi cosa potrebbe causare questa differenza di comportamento? E come potrebbe essere fissato per assicurarsi che le entità siano sempre tracciate?
Alcune note:
Wrapper
è chiaramente inutile qui, ma è un esempio minimale di un costrutto più significativo nel mio vero programma che provoca lo stesso comportamento. foreach
(in modo che quello con il wrapper duri per ultimo) fa sì che le entità vengano tracciate in entrambi i loop, quindi in tal caso il primo ciclo ha chiaramente un effetto collaterale sul secondo ciclo. foreach
per iterare su context.Thing.ToArray().Select(a => new Wrapper(a))
(con un ToArray
aggiunto) fornisce il risultato previsto (entità tracciate), quindi questo sembra essere correlato al metodo di iterazione - ma come? Apparentemente, il codice EF interpreta Select(a => new Wrapper(a))
lo stesso di Select(a => new { a.Id, a.Name } )
. Non può vedere che un Wrapper memorizza un riferimento all'indietro.
In altre parole, vede (pensa) che stai convertendo immediatamente l'entità in modo che decida di non tracciarla.
È specificato qui , ma devi capire che la new{}
viene elaborata anche da EF. E il tuo new Wrapper(a)
non lo è.
Potresti provare a => new Wrapper() {Thing = a}
, non ne sono sicuro al 100%.
... il primo ciclo ha chiaramente un effetto collaterale sul secondo ciclo.
Sì, purché facciano parte della stessa connessione. Il tracker non "dimenticherà" le entità. Puoi leggere a riguardo qui .