In Entity Framework 6 è stato introdotto il metodo AddRange. È ottimo per i grandi inserti perché il metodo DbSet.Add attiva sempre DetectChanges che rallenta notevolmente il processo. Volevo solo utilizzare un codice esistente basato sull'interfaccia IDbSet quando ho realizzato che non ha il metodo AddRange. Esiste solo nella classe DbSet.
Ho cercato su Google un po 'e ho trovato questa discussione - http://forums.asp.net/t/1978828.aspx?Why+is+there+no+AddRange+method+for+System+Data+Entity+IDbSet+T+ - ma non c'è una chiara conclusione sul motivo per cui in realtà il metodo AddRange non esiste nell'interfaccia IDbSet.
È un bug o c'è qualche buona ragione per non esserci? Qualche idea?
AGGIORNARE
Qui https://entityframework.codeplex.com/workitem/2781 Microsoft mi ha dato una risposta:
Questo è di design. L'approccio all'interfaccia non è stato ottimale per DbSet perché l'aggiunta di membri interrompe qualsiasi applicazione esistente che implementa l'interfaccia.
Dato che vogliamo essere in grado di aggiungere membri a DbSet, passiamo a un approccio di classe base in cui DbSet è una classe base che puoi direttamente prendere in giro o ereditare.
Ecco alcuni collegamenti che mostrano come utilizzare DbSet anziché IDbSet:
Da Entity Framework Design Meeting Notes, il 16 maggio 2013 :
Il team ha riconosciuto il potenziale per rompere le modifiche:
Le classi DbSet (generiche e non generiche) ereditano da un'interfaccia IDbSet (generica o non generica). IDbSet è inteso solo per creare duplicati di test, essere questi falsi o falsi.
Tuttavia, in EF6 DbSet è cambiato in quattro modi in cui se riflessi in modifiche equivalenti per IDbSet si verificherebbero i cambiamenti:
- FindAsync aggiunto
- AddRange / RemoveRange aggiunto
- Tipo di ritorno locale cambiato in DbLocalView (questa modifica può essere ripristinata in ogni caso)
Hanno discusso di una serie di potenziali modifiche in dettaglio, ma alla fine hanno deciso di evitare il cambio di rotta e di "rendere DbSet più schivo":
La decisione era di rendere DbSet più irriducibile. Tuttavia, IDbSet non sarà obsoleto perché ciò creerebbe lavoro per quelli che attualmente utilizzano IDbSet che non hanno bisogno di usare i nuovi membri. Aggiungiamo una guida a IDbSet che indica che l'utilizzo di DbSet è la via da seguire per ottenere un nuovo codice e, in base al feedback, potremmo scegliere IDbSet obsoleto in una versione futura.
E se guardi il codice per IDbSet
, hanno aggiunto commenti nella parte superiore dell'interfaccia:
IDbSet era originariamente concepito per consentire la creazione di duplicati di test (mock o falsi) per DbSet. Tuttavia, questo approccio presenta problemi in quanto l'aggiunta di nuovi membri a un'interfaccia interrompe il codice esistente che implementa già l'interfaccia senza i nuovi membri.
Pertanto, a partire da EF6, nessun nuovo membro verrà aggiunto a questa interfaccia e si consiglia di utilizzare DbSet come classe base per i duplicati di prova.
Questo non è sicuramente un bug ed è fatto in questo modo dal design. È difficile rispondere a questo tipo di domande senza essere uno sviluppatore della libreria .NET, ma la mia ipotesi è che volessero mantenere semplici le interfacce. Forse qualcuno nel team di .NET vedrà questo e interverrà. I problemi di retrocompatibilità saranno legati all'interfaccia / implementazione concreta, che è anche sicuramente una possibilità in questo scenario. Tuttavia, questo ragionamento probabilmente non si baserà sul perché IList / ICollection non li definisce.
Un argomento è che aggiungendo AddRange (così come RemoveRange / InsertRange) all'interfaccia, si sta costringendo l'implementatore a definire tali metodi. Le interfacce dovrebbero essere semplici e facili da definire. I metodi AddRange, etc dovrebbero esistere solo su raccolte concrete dove è possibile vedere miglioramenti delle prestazioni. Ad esempio, un elenco può ottimizzare il suo AddRange aumentando opportunamente la sua capacità interna, ecc. Dove un semplice ciclo su voci e chiamate Add può essere più costoso (ad esempio tramite un metodo di estensione).
Probabilmente lo fanno per lo stesso motivo per cui IList, ICollection, ecc. Non hanno un AddRange direttamente sull'interfaccia ma eseguono le implementazioni concrete.
C'è una soluzione per questo, e cioè per astrarre sia l'interfaccia che la classe concreta usando la tua interfaccia / classe. Puoi dare alla tua interfaccia un AddRange ed estendere DBSet / implementare la tua interfaccia in un'altra classe. Questo può o non può funzionare a seconda di come si utilizza IDBSet / DBSet nel codice. Anche se, un piccolo refactoring può fare questo lavoro. Qualcosa come questo --
public class MyDbSet<TEntity> : DbSet<TEntity>, IMyDbSet<TEntity> where TEntity : class
{
}
public interface IMyDbSet<TEntity> : IDbSet<TEntity> where TEntity : class
{
IEnumerable<TEntity> AddRange(IEnumerable<TEntity> items);
}
Ora puoi semplicemente usare IMyDbSet nel tuo codice. Non è necessario implementare AddRange poiché è già implementato estendendo DbSet. I metodi esterni che accettano solo IDbSet / DbSet dovrebbero comunque accettare MyDbSet / IMyDbSet.