Voglio eliminare un'entità in EF Core senza caricarla prima dal database. So che domande simili sono state poste prima, ma abbiate pazienza, perché questo caso è diverso. Oltre al solito ID, l'entità ha anche una versione di riga, che causa problemi.
L'entità è definita in questo modo:
public int MyEntity {
public int Id { get; set; }
//Other irrelevant properties
public byte[] RowVersion { get; set; }
}
L'entità è configurata con l'API fluente:
class MyEntityConfiguration : IEntityTypeConfiguration<MyEntity> {
public void Configure( EntityTypeBuilder<MyEntity> builder ) {
builder.Property( e => e.RowVersion )
.IsRequired()
.IsRowVersion();
}
}
La versione della riga mi permette di fare un controllo ottimistico della concorrenza. Il problema è che quando provo a eliminare l'entità senza caricarla prima in questo modo ...
void RemoveMyEntity( int id ) {
MyEntity removeEntity = new MyEntity {
Id = id
};
m_context.MyEntities.Attach( removeEntity );
m_context.MyEntities.Remove( removeEntity );
}
... il controllo della concorrenza cade in piedi. Ricevo questo messaggio di errore in DbUpdateConcurrencyException
:
Operazione del database prevista per 1 riga (e) ma effettivamente 0 riga (e). I dati potrebbero essere stati modificati o eliminati da quando le entità sono state caricate.
Il motivo è che EF Core genera questa query per eliminare l'elemento:
exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [MyEntity]
WHERE [Id] = @p57 AND [RowVersion] IS NULL;
SELECT @@ROWCOUNT',N'@p57 int',@p57=1 -- <--The ID of the item to delete
Il problema è chiaramente in AND [RowVersion] IS NULL
. Questa condizione non può mai essere true
, poiché (come ho chiaramente detto a EF durante la configurazione dell'entità), la colonna è obbligatoria e quindi non può essere NULL
.
Naturalmente, non ho aggiunto una versione di riga nell'entità che voglio eliminare e in realtà non voglio aggiungere la versione di riga, perché ciò significherebbe che devo ottenere i dati dal DB, che non è necessario in questo Astuccio. Non mi dispiace nemmeno controllare la concorrenza qui, perché non fa male se l'elemento è stato eliminato prima.
Quindi la domanda è: c'è un modo per ignorare il controllo della concorrenza per questa operazione (ma non per altre operazioni nella stessa transazione) o per far funzionare l'eliminazione in un altro modo senza dover prima ottenere la versione della riga dal DB.
Non sono sicuro di EF Core ma utilizzo la seguente soluzione alternativa per EF 6: Ho creato NoConcurencyDbContext aggiuntivo che eredita dal principale, ignoro OnModelCreating e configuro tutte le proprietà RowVersion su ConcurencyMode.None
publicclass NoConcurencyDbContext : MainDbContext {
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<EntityWithRowVersio>().Property(t => t.RowVersion).IsConcurrencyToken(false);
}
}
È possibile scrivere un modello T4 e generare tale contesto automaticamente dal modello EF. Quindi l'idea è semplice modificare la configurazione del modello all'interno di dbcontext figlio per un'operazione particolare. Spero che possa essere fatto anche in EF Core