NON usando pattern di repository, usa l’ORM così com’è (EF)

Ho sempre usato il pattern di repository ma per il mio ultimo progetto volevo vedere se potevo perfezionarne l’utilizzo e la mia implementazione di “Unit Of Work”. Più ho iniziato a scavare ho iniziato a pormi la domanda: “Ne ho davvero bisogno?”

Ora tutto questo inizia con un paio di commenti su Stackoverflow con una traccia del post di Ayende Rahien sul suo blog, con 2 specifici,

  • repository-è-il-nuovo-Singleton
  • ask-Ayende-life-senza-repository-sono-che-vale-living

Questo potrebbe probabilmente essere parlato per sempre e sempre e dipende da diverse applicazioni. Che cosa mi piacerebbe sapere,

  1. questo approccio sarebbe adatto per un progetto Entity Framework?
  2. utilizzando questo approccio è la logica di business ancora in corso in un livello di servizio, o metodi di estensione (come spiegato di seguito, lo so, il metodo di estensione sta usando la sessione di NHib)?

È fatto facilmente usando i metodi di estensione. Pulito, semplice e riutilizzabile.

public static IEnumerable GetAll( this ISession instance, Expression<Func> where) where T : class { return instance.QueryOver().Where(where).List(); } 

Usando questo approccio e Ninject come DI, devo rendere un’interfaccia del Context e Ninject nei miei controller?

Ho percorso molti percorsi e creato molte implementazioni di repository su diversi progetti e … Ho gettato la spugna e ci ho rinunciato, ecco perché.

Coding per l’eccezione

Programmate l’1% di possibilità che il vostro database cambierà da una tecnologia all’altra? Se stai pensando allo stato futuro della tua azienda e dì sì che è una possibilità, allora a) devono avere un sacco di soldi per permettersi di fare una migrazione a un’altra tecnologia DB o b) stai scegliendo una tecnologia DB per divertimento o c ) qualcosa è andato terribilmente storto con la prima tecnologia che hai deciso di usare.

Perché buttare via la ricca syntax LINQ?

LINQ ed EF sono stati sviluppati in modo da poter fare roba pulita con esso per leggere e attraversare i grafici degli oggetti. Creare e mantenere un repository che possa darti la stessa flessibilità per farlo è un’attività mostruosa. Nella mia esperienza, ogni volta che ho creato un repository ho SEMPRE avuto perdite di logica aziendale nel livello del repository per rendere le query più performanti e / o ridurre il numero di accessi al database.

Non voglio creare un metodo per ogni singola permutazione di una query che devo scrivere. Potrei anche scrivere stored procedure. Non voglio GetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserId e così via … Voglio solo ottenere l’ quadro principale e attraversare e includere il grafico dell’object come mi pare.

La maggior parte degli esempi di repository sono cazzate

A meno che tu non stia sviluppando qualcosa di veramente nudo come un blog o qualcosa del genere, le tue domande non saranno mai così semplici come il 90% degli esempi che trovi su Internet che circonda il modello di repository. Non posso sottolineare abbastanza! Questo è qualcosa che si deve strisciare nel fango per capire. Ci sarà sempre quella query che rompe il tuo repository / soluzione perfettamente pensata che hai creato, e non fino a quel punto in cui tu hai indovinato te stesso e il debito tecnico / erosione ha inizio.

Non mi sottoponiti a test

Ma per quanto riguarda il test unitario se non ho un repository? Come mi deriderò? Semplicemente no. Guardiamo da entrambi gli angoli:

Nessun repository – Puoi prendere in giro DbContext usando un IDbContext o qualche altro trucco, ma in questo caso sei davvero in grado di testare LINQ su Oggetti e non LINQ su Entità perché la query è determinata in fase di esecuzione … OK, quindi non va bene! Quindi ora è il test di integrazione per coprire questo.

Con repository – Ora puoi prendere in giro i tuoi repository e testare l’unità tra i livelli. Ottimo vero? Beh, non proprio … Nei casi precedenti in cui è necessario filtrare la logica nel livello del repository per rendere le query più performanti e / o meno hit al database, in che modo i test di unità possono coprire ciò? Ora è nel livello repo e non vuoi testare IQueryable giusto? Inoltre, sii onesto, i tuoi test unitari non copriranno le query che hanno una clausola .Include() e .Include() un gruppo di relazioni e colpisce di nuovo il database per fare tutte queste altre cose, blah, blah, blah comunque perché la query è generata in fase di runtime. Inoltre, dal momento che hai creato un repository per mantenere ignorante la persistenza dei livelli superiori, se ora vuoi cambiare la tecnologia del tuo database, ci spiace che i tuoi test unitari non garantiscano gli stessi risultati in fase di runtime, ai test di integrazione. Quindi l’intero punto del repository sembra strano ..

2 centesimi

Abbiamo già perso molte funzionalità e syntax quando si utilizza EF su semplici stored procedure (inserimenti di massa, eliminazioni di massa, CTE, ecc.) Ma inserisco anche codice in C # in modo da non dover digitare binari. Usiamo EF in modo che possiamo avere la possibilità di utilizzare diversi provider e di lavorare con i grafici degli oggetti in un modo correlato tra molte cose. Certe astrazioni sono utili e altre no.

Spero che questo aiuti qualcuno su internet da qualche parte …

Il modello di repository è un’astrazione . Lo scopo è ridurre la complessità e rendere ignorante il resto del codice persistente. Come bonus, consente di scrivere test unitari anziché test di integrazione .

Il problema è che molti sviluppatori non riescono a capire lo scopo dei pattern e creano repository che perdono informazioni specifiche sulla persistenza fino al chiamante (in genere esponendo IQueryable ). In questo modo non ottengono alcun vantaggio sull’uso diretto di OR / M.

Aggiornamento per indirizzare un’altra risposta

Coding per l’eccezione

L’uso dei repository non significa essere in grado di cambiare la tecnologia di persistenza (ossia modificare il database o utilizzare un servizio Web, ecc.). Si tratta di separare la logica aziendale dalla persistenza per ridurre la complessità e l’accoppiamento.

Test unitari vs test di integrazione

Non scrivi test unitari per i repository. periodo.

Ma introducendo i repository (o qualsiasi altro livello di astrazione tra persistance e business) si è in grado di scrivere test unitari per la logica di business. vale a dire che non devi preoccuparti del fallimento dei test a causa di un database configurato in modo errato.

Per quanto riguarda le domande. Se usi LINQ devi anche assicurarti che le tue query funzionino, proprio come hai a che fare con i repository. e ciò viene fatto usando i test di integrazione.

La differenza è che se non hai mischiato la tua attività con le istruzioni LINQ puoi essere sicuro al 100% che il tuo codice di persistenza sta fallendo e non qualcos’altro.

Se analizzi i tuoi test vedrai anche che sono molto più puliti se non hai dubbi contrastanti (es. LINQ + Business logic)

Esempi di repository

La maggior parte degli esempi sono cazzate. questo è molto vero. Tuttavia, se si google qualsiasi modello di progettazione, troverete un sacco di esempi di merda. Non c’è motivo di evitare l’uso di un modello.

Creare un’implementazione corretta del repository è molto semplice. In realtà, devi solo seguire una singola regola:

Non aggiungere nulla nella class repository fino al momento in cui ne hai bisogno

Molti programmatori sono pigri e cercano di creare un repository generico e usano una class base con molti metodi di cui potrebbero aver bisogno. YAGNI. Scrivi una volta la class del repository e la mantieni finché dura la vita dell’applicazione (può essere anni). Perché fottere da essere pigro. Mantienilo pulito senza alcuna ereditarietà di class base. Renderà molto più facile la lettura e la manutenzione.

(L’affermazione di cui sopra è una linea guida e non una legge, una class base può benissimo essere motivata. Pensa prima di aggiungerla, in modo da aggiungerla per i giusti motivi)

Roba vecchia

Conclusione:

Se non ti dispiace avere istruzioni LINQ nel tuo codice aziendale, né preoccuparti dei test unitari, non vedo alcun motivo per non utilizzare direttamente Entity Framework.

Aggiornare

Ho scritto sul blog sia sul pattern del repository che su “astrazione”: http://blog.gauffin.org/2013/01/repository-pattern-done-right/

Aggiornamento 2

Per il tipo a singola quadro con 20+ campi, come si progetterà il metodo di query per supportare qualsiasi combinazione di permutazione? Non si desidera limitare la ricerca solo per nome, per quanto riguarda la ricerca con proprietà di navigazione, elencare tutti gli ordini con articolo con codice prezzo specifico, 3 livelli di ricerca proprietà di navigazione. L’intera ragione per cui IQueryable stata inventata era quella di essere in grado di comporre qualsiasi combinazione di ricerca con il database. Tutto sembra fantastico in teoria, ma il bisogno dell’utente supera la teoria.

Ancora: un’ quadro con oltre 20 campi è modellata in modo errato. È un’entity framework DIO. Scomposizione.

Non sto sostenendo che IQueryable non è stato creato per il quering. Sto dicendo che non è giusto per un livello di astrazione come il pattern di repository dal momento che perde. Non esiste un provider LINQ to Sql completo al 100% (come EF).

Tutti hanno cose specifiche sull’implementazione come come usare il caricamento “pigro / pigro” o come fare istruzioni “IN” SQL. L’esposizione di IQueryable nel repository costringe l’utente a conoscere tutte queste cose. Quindi l’intero tentativo di astrarre la fonte dei dati è un fallimento completo. Basta aggiungere la complessità senza trarre alcun vantaggio dall’uso diretto di OR / M.

O implementare il modello di repository correttamente o semplicemente non utilizzarlo affatto.

(Se vuoi davvero gestire grandi quadro, puoi combinare il modello di repository con il modello Specification . Ciò ti fornisce un’astrazione completa che è anche verificabile).

IMO sia l’astrazione del UnitOfWork astrazione di UnitOfWork hanno un posto molto prezioso in ogni sviluppo significativo. Le persone discuteranno dei dettagli di implementazione, ma proprio come ci sono molti modi per skinare un gatto, ci sono molti modi per implementare un’astrazione.

La tua domanda è specificamente da usare o non usare e perché.

Come avrai senza dubbio capito che hai già entrambi questi modelli incorporati in Entity Framework, DbContext è UnitOfWork e DbSet è il Repository . Generalmente non è necessario eseguire il test dell’unità di UnitOfWork o Repository in quanto facilitano semplicemente tra le classi e le implementazioni di accesso ai dati sottostanti. Quello che ti ritrovi a dover fare, ancora e ancora, è prendere in giro queste due astrazioni quando collaudi la logica dei tuoi servizi.

Puoi prendere in giro, fingere o qualsiasi altra cosa con le librerie esterne aggiungendo strati di dipendenze di codice (che non controlli) tra la logica che esegue il test e la logica da testare.

Quindi, un punto minore è che avere la propria astrazione per UnitOfWork e Repository ti dà il massimo controllo e flessibilità quando UnitOfWork giro i tuoi test unitari.

Tutto molto bene, ma per me, il vero potere di queste astrazioni è che forniscono un modo semplice per applicare le tecniche di programmazione orientata all’aspetto e aderire ai principi SOLID .

Quindi hai il tuo IRepository :

 public interface IRepository where T : class { T Add(T entity); void Delete(T entity); IQueryable AsQueryable(); } 

E la sua implementazione:

 public class Repository : IRepository where T : class { private readonly IDbSet _dbSet; public Repository(PPContext context) { _dbSet = context.Set(); } public T Add(T entity) { return _dbSet.Add(entity); } public void Delete(T entity) { _dbSet.Remove(entity); } public IQueryable AsQueryable() { return _dbSet.AsQueryable(); } } 

Niente di straordinario finora, ma ora vogliamo aggiungere un po ‘di registrazione – facile con un decoratore di registrazione.

 public class RepositoryLoggerDecorator : IRepository where T : class { Logger logger = LogManager.GetCurrentClassLogger(); private readonly IRepository _decorated; public RepositoryLoggerDecorator(IRepository decorated) { _decorated = decorated; } public T Add(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() ); T added = _decorated.Add(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); return added; } public void Delete(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); _decorated.Delete(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); } public IQueryable AsQueryable() { return _decorated.AsQueryable(); } } 

Tutto fatto e senza modifiche al nostro codice esistente . Esistono numerosi altri problemi trasversali che possiamo aggiungere, come la gestione delle eccezioni, la memorizzazione dei dati, la convalida dei dati o qualsiasi altra cosa e durante il processo di progettazione e costruzione, la cosa più preziosa che ci consente di aggiungere funzionalità semplici senza modificare il codice esistente è la nostra astrazione IRepository .

Ora, molte volte ho visto questa domanda su StackOverflow: “come si fa a far funzionare Entity Framework in un ambiente multi-tenant?”.

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

Se si dispone di un’astrazione di Repository la risposta è “è facile aggiungere un decoratore”

 public class RepositoryTennantFilterDecorator : IRepository where T : class { //public for Unit Test example public readonly IRepository _decorated; public RepositoryTennantFilterDecorator(IRepository decorated) { _decorated = decorated; } public T Add(T entity) { return _decorated.Add(entity); } public void Delete(T entity) { _decorated.Delete(entity); } public IQueryable AsQueryable() { return _decorated.AsQueryable().Where(o => true); } } 

IMO dovresti sempre piazzare una semplice astrazione su qualsiasi componente di terze parti a cui verrà fatto riferimento in più di una manciata di luoghi. Da questo punto di vista un ORM è il candidato perfetto in quanto viene fatto riferimento in così tanto del nostro codice.

La risposta che di solito viene in mente quando qualcuno dice “perché dovrei avere un’astrazione (ad esempio un Repository ) su questa o quella libreria di terze parti” è “perché non dovresti?”

I decoratori PS sono estremamente semplici da applicare utilizzando un contenitore IoC, come SimpleInjector .

 [TestFixture] public class IRepositoryTesting { [Test] public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository() { Container container = new Container(); container.RegisterLifetimeScope(); container.RegisterOpenGeneric( typeof(IRepository<>), typeof(Repository<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryLoggerDecorator<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryTennantFilterDecorator<>)); container.Verify(); using (container.BeginLifetimeScope()) { var result = container.GetInstance>(); Assert.That( result, Is.InstanceOf(typeof(RepositoryTennantFilterDecorator))); Assert.That( (result as RepositoryTennantFilterDecorator)._decorated, Is.InstanceOf(typeof(RepositoryLoggerDecorator))); } } } 

Prima di tutto, come suggerito da alcune risposte, EF stesso è un modello di repository, non è necessario creare ulteriore astrazione solo per nominarlo come repository.

Repository Mockable per i test unitari, ne abbiamo davvero bisogno?

Lasciamo che EF comunichi per testare il DB nei test unitari per testare la nostra logica aziendale direttamente contro il DB di test SQL. Non vedo alcun beneficio di aver preso in giro qualsiasi modello di repository. Cosa c’è di veramente sbagliato nel fare test unitari contro il database di test? Dal momento che le operazioni bulk non sono possibili e finiamo per scrivere SQL raw. SQLite in memoria è un candidato perfetto per fare test unitari contro database reali.

Astrazione inutile

Vuoi creare un repository solo in modo che in futuro puoi facilmente sostituire EF con NHbibernate ecc o altro? Sembra un ottimo piano, ma è davvero conveniente?

Linq uccide i test unitari?

Mi piacerebbe vedere qualche esempio su come può uccidere.

Iniezione delle dipendenze, IoC

Wow queste sono grandi parole, sicuramente sembrano grandi in teoria, ma a volte devi scegliere tra un grande design e una grande soluzione. Abbiamo usato tutto questo e abbiamo finito col buttare tutto alla spazzatura e scegliendo un approccio diverso. Size vs Speed ​​(dimensioni del codice e velocità di sviluppo) è importante nella vita reale. Gli utenti hanno bisogno di flessibilità, a loro non importa se il tuo codice è ottimo nel design in termini di DI o IoC.

A meno che non stiate sviluppando Visual Studio

Tutti questi grandi progetti sono necessari se si sta costruendo un programma complesso come Visual Studio o Eclipse che sarà sviluppato da molte persone e che deve essere altamente personalizzabile. Tutti i grandi schemi di sviluppo sono entrati in scena dopo anni di sviluppo che questi IDE hanno attraversato, e si sono evoluti nel luogo in cui tutti questi modelli di progettazione sono così importanti. Ma se stai facendo un semplice stipendio basato sul web o una semplice app aziendale, è meglio che tu evolvi nel tuo sviluppo con il tempo, invece di spendere del tempo per costruirlo per milioni di utenti, dove verrà distribuito solo per centinaia di utenti.

Deposito come vista filtrata – ISecureRepository

Dall’altro lato, il repository dovrebbe essere una vista filtrata di EF che protegge l’accesso ai dati applicando il filler necessario in base all’utente / ruolo corrente.

Ma farlo rende ancora più complicato il repository man mano che finisce in un’enorme base di codice da mantenere. Le persone finiscono per creare repository differenti per diversi tipi di utenti o combinazioni di tipi di entity framework. Non solo, finiamo anche con un sacco di DTO.

La seguente risposta è un’implementazione di esempio di Repository filtrato senza creare un set completo di classi e metodi. Potrebbe non rispondere direttamente alla domanda, ma può essere utile nel derivarne una.

Disclaimer: sono autore di Entity REST SDK.

http://entityrestsdk.codeplex.com

Tenendo presente in mente, abbiamo sviluppato un SDK che crea il repository della vista filtrata basata su SecurityContext che contiene i filtri per le operazioni CRUD. E solo due tipi di regole semplificano qualsiasi operazione complessa. Il primo è l’accesso all’entity framework e l’altro è la regola di lettura / scrittura per la proprietà.

Il vantaggio è che non si riscrivono logiche aziendali o repository per tipi di utenti diversi, basta semplicemente bloccare o concedere loro l’accesso.

 public class DefaultSecurityContext : BaseSecurityContext { public static DefaultSecurityContext Instance = new DefaultSecurityContext(); // UserID for currently logged in User public static long UserID{ get{ return long.Parse( HttpContext.Current.User.Identity.Name ); } } public DefaultSecurityContext(){ } protected override void OnCreate(){ // User can access his own Account only var acc = CreateRules(); acc.SetRead( y => x=> x.AccountID == UserID ) ; acc.SetWrite( y => x=> x.AccountID == UserID ); // User can only modify AccountName and EmailAddress fields acc.SetProperties( SecurityRules.ReadWrite, x => x.AccountName, x => x.EmailAddress); // User can read AccountType field acc.SetProperties( SecurityRules.Read, x => x.AccountType); // User can access his own Orders only var order = CreateRules(); order.SetRead( y => x => x.CustomerID == UserID ); // User can modify Order only if OrderStatus is not complete order.SetWrite( y => x => x.CustomerID == UserID && x.OrderStatus != "Complete" ); // User can only modify OrderNotes and OrderStatus order.SetProperties( SecurityRules.ReadWrite, x => x.OrderNotes, x => x.OrderStatus ); // User can not delete orders order.SetDelete(order.NotSupportedRule); } } 

Queste regole LINQ sono valutate rispetto al database nel metodo SaveChanges per ogni operazione e queste regole fungono da firewall davanti al database.

C’è un ampio dibattito su quale metodo sia corretto, quindi lo considero come entrambi sono accettabili, quindi uso sempre quello che mi piace di più (che non è un repository, UoW).

In EF UoW è implementato tramite DbContext e i DbSet sono repository.

Per quanto riguarda il modo di lavorare con il livello dati, lavoro direttamente sull’object DbContext, per le query complesse creerò metodi di estensione per la query che possono essere riutilizzati.

Credo che Ayende abbia anche alcuni post su come l’astrazione delle operazioni CUD sia ctriggers.

Faccio sempre un’interfaccia e ho ereditato il mio contesto, così posso utilizzare un contenitore IoC per DI.

Linq è un “deposito” al giorno d’oggi.

ISession + Linq è già il repository e non è necessario né i metodi QueryData(Query q) né la QueryData(Query q) . Essendo un po ‘paranoico all’uso di DAL, preferisco comunque l’interfaccia del repository. (Dal punto di vista della manutenibilità dobbiamo anche avere qualche facciata su specifiche interfacce di accesso ai dati).

Qui è il repository che usiamo – esso ci disimpegna dall’uso diretto di nhibner, ma fornisce l’interfaccia di linq (come accesso di accesso in casi eccezionali, che alla fine sono soggetti a refactoring).

 class Repo { ISession _session; //via ioc IQueryable Query() { return _session.Query(); } } 

Il repository (o comunque si sceglie di chiamarlo) in questo momento per me si occupa principalmente dell’astrazione del livello di persistenza.

Lo uso insieme agli oggetti query, quindi non ho un accoppiamento con una particolare tecnologia nelle mie applicazioni. E facilita anche i test.

Quindi, tendo ad avere

 public interface IRepository : IDisposable { void Save(TEntity entity); void SaveList(IEnumerable entities); void Delete(TEntity entity); void DeleteList(IEnumerable entities); IList GetAll() where TEntity : class; int GetCount() where TEntity : class; void StartConversation(); void EndConversation(); //if query objects can be self sustaining (ie not need additional configuration - think session), there is no need to include this method in the repository. TResult ExecuteQuery(IQueryObject query); } 

È ansible aggiungere metodi asincroni con i callback come delegati. Il repository è facile da implementare in modo generico , quindi sono in grado di non toccare una linea dell’implementazione dall’app all’app. Bene, questo è vero almeno quando si usa NH, l’ho fatto anche con EF, ma mi ha fatto odiare EF. 4. La conversazione è l’inizio di una transazione. Molto interessante se alcune classi condividono l’istanza del repository. Inoltre, per NH, un repository nella mia implementazione è uguale a una sessione aperta alla prima richiesta.

Quindi gli oggetti di query

 public interface IQueryObject { /// Provides configuration options. ///  /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository. /// If not used through a repository, it can be useful as a configuration option. ///  void Configure(object parameter); /// Implementation of the query. TResult GetResult(); } 

Per la configurazione che utilizzo in NH solo per passare l’ISession. In EF non ha senso più o meno.

Una query di esempio potrebbe essere .. (NH)

 public class GetAll : AbstractQueryObject> where TEntity : class { public override IList GetResult() { return this.Session.CreateCriteria().List(); } } 

Per fare una query EF, dovresti avere il contesto nella Base astratta, non nella sessione. Ma ovviamente l’ifc sarebbe lo stesso.

In questo modo le domande sono a loro volta incapsulate e facilmente testabili. Meglio di tutto, il mio codice si basa solo sulle interfacce. Tutto è molto pulito. Gli oggetti di dominio (business) sono proprio questo, ad esempio non c’è un mix di responsabilità come quando si usa il pattern di record attivo che è difficilmente testabile e si mescola il codice di accesso ai dati (query) nell’object dominio e così facendo si mescolano le preoccupazioni (object che recupera si??). Tutti sono ancora liberi di creare POCO per il trasferimento dei dati.

Tutto sumto, molto riuso di codice e semplicità è fornito con questo approccio alla perdita di nulla che possa immaginare. Qualche idea?

E grazie mille ad Ayende per i suoi fantastici post e la costante dedizione. Le sue idee sono qui (object della query), non mie.

Ciò che più si applica su EF non è un pattern di repository. È un pattern di facciata (che riassume le chiamate ai metodi EF in versioni più semplici e facili da usare).

EF è quello che applica il modello di deposito (e anche il modello di unità di lavoro). Cioè, EF è l’unico che estrae il livello di accesso ai dati in modo che l’utente non abbia idea di avere a che fare con SQLServer.

E a questo punto, la maggior parte dei “repository” su EF non sono nemmeno delle buone facciate in quanto si limitano a mappare, abbastanza semplicemente, a singoli metodi in EF, fino al punto di avere le stesse firme.

I due motivi, quindi, per applicare questo cosiddetto schema “Repository” su EF è quello di consentire test più semplici e di stabilire un sottoinsieme di chiamate “in scatola” su di esso. Non male in sé, ma chiaramente non un deposito.

Per me, è una decisione semplice, con relativamente pochi fattori. I fattori sono:

  1. I repository sono per le classi di dominio.
  2. In alcune delle mie app, le classi di dominio sono le stesse delle mie classi di persistenza (DAL), in altre non lo sono.
  3. Quando sono uguali, EF mi fornisce già i repository.
  4. EF provides lazy loading and IQueryable. I like these.
  5. Abstracting/’facading’/re-implementing repository over EF usually means loss of lazy and IQueryable

So, if my app can’t justify #2, separate domain and data models, then I usually won’t bother with #5.