Ho un'architettura client server in cui il server fornisce un'API Rest per consentire ai client di sincronizzare tutti i dati del database. Lo salvano nel loro database SQLite locale. Il modello è in un progetto condiviso e può cambiare a volte. Pertanto, i client devono aggiornare lo schema del database SQLite locale. Questo ovviamente accade solo dopo l'aggiornamento del software del client (il file di database rimane invariato).
Si ottiene semplicemente eliminando generalmente il file di database e ricreandolo in seguito.
_context.Database.EnsureDeleted();
_context.Database.EnsureCreated();
AttachNewDataFromServerToDatabaseContext(_context);
_context.SaveChanges();
Il servizio API Rest è istanziato come singelton e utilizza sempre lo stesso oggetto di contesto del database. La prima sincronizzazione funziona bene. Ma i prossimi falliscono:
System.InvalidOperationException: The instance of entity type '***' cannot be tracked because another instance with the key value '{id: ***}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Quindi il Change Tracker è ancora a conoscenza delle entità "vecchie" sebbene l'intero database sia stato eliminato.
I miei pensieri al riguardo:
Cosa ne pensi? Grazie per l'aiuto!
- L'istanza di un nuovo contesto di dati ogni volta che il database è sincronizzato potrebbe risolverlo perché il Tracker modifiche inizia da "zero". Non è una buona soluzione ai miei occhi.
Preferirei dire che questa è la soluzione "giusta". I metadati di contesto (aka Model
) per impostazione predefinita sono memorizzati nella cache per tipo di contesto, la connessione db viene gestita dal pool di connessioni e viene aperta / chiusa solo quando necessario comunque. Quindi l'unico vantaggio del riutilizzo dell'istanza di contesto è quello di evitare di creare diverse istanze DbSet
.
Allo stesso tempo, il tracker manterrebbe molte istanze di "entità" e impedirebbe loro di essere garbage collection senza alcuna necessità dopo la chiamata SaveChanges
. Per non contare potenziali problemi di accesso multithread.
Quindi IMHO è la strada da percorrere: istanziare un nuovo contesto, fare qualcosa con esso e smaltirlo.
- Sarebbe grandioso se anche il servizio di eliminazione di sicurezza "Ripristina" il Tracker modifiche o se fosse possibile eseguirlo manualmente.
In effetti sarebbe fantastico. Ma attualmente né EnsureDeleted
fa né EF Core offre un modo pubblico per farlo manualmente.
Esiste tuttavia un modo interno, con il solito rischio che possa essere modificato in una futura versione di EF Core. Aggiunta
using Microsoft.EntityFrameworkCore.Infrastructure;
ti permetterebbe di usare qualcosa del genere
_context.ChangeTracker.GetInfrastructure().ResetState();
probabilmente prima di _context.Database.EnsureDeleted();
. Il che dimostra sostanzialmente che dovresti davvero usare la prima opzione (nuovo contesto).
Aggiornamento (EF Core 3.0): ChangeTracker
non espone più StateManager
nemmeno internamente, quindi qui abbiamo bisogno
using Microsoft.EntityFrameworkCore.Internal;
e rispettivamente
_context.GetDependencies().StateManager.ResetState();