24

Uso l'approccio del database Entity Framework per generare un modello DbContext/POCO per un'applicazione MVC. Voglio evitare di avere dipendenze su DbContext nei miei controller per consentirmi di passare a un altro fornitore di persistenza come ho bisogno di (ad esempio per scopi di test unitari).Utilizzo di Entity Framework con Castle Windsor

Per fare ciò, voglio utilizzare il contenitore IoC Castle Windsor. Ho in programma di registrare DbContext come servizio IUnitOfWork e di registrare un servizio IRepository generico, le cui implementazioni userò per accedere e lavorare con le radici aggregate nel modello.

Sono nuovo di Windsor e non sono stati in grado di trovare molte informazioni su come utilizzare con EF, e ho un paio di domande:

  • questo è un approccio ragionevole se voglio disaccoppiare EF dall'applicazione?
  • come installare/registrare i servizi IUnitOfWork e generici IRepository?
+3

per chi ha segnato questo giù ... ti piacerebbe fornire un commento che dice perché? Se pensi di aver mancato il punto da qualche parte, potresti aiutarmi spiegando perché. –

+0

Penso che sia stato rifiutato perché questa è una domanda di discussione, non proprio appropriata per StackOverflow, specialmente il primo punto. Il secondo punto può essere trovato nel sito di Castle Windsor, hanno una documentazione eccellente. Scritto il contenuto della tua domanda: la simulazione di DbContext per i test delle unità è [praticamente impossibile] (http://stackoverflow.com/a/13352779/861716). Io uso i progetti di "unit test" su un database per il test DAL oltre a progetti di test unitari puri per altri BL. Caste Windsor inietta contesti da una fabbrica di contesto, non interfacciata, servizi interfacciati, validatori ecc. –

+0

Grazie per il tuo commento @GertArnold. Per quanto riguarda il problema del mocking, non voglio testare il DAL, ma essere in grado di fornire repository simulati al livello dell'applicazione (ad esempio i controller), per testarlo. La documentazione del castello è valida in generale, ma si occupa dell'integrazione con NHibernate ed è piuttosto schematica in quell'area. –

risposta

25

Quindi, alcune conclusioni. Ho pensato di scriverlo nel caso in cui fosse utile a qualcun altro che cercasse di usare insieme/unit test EF, Windsor e MVC.

Prima di tutto, poiché DbContext implementa entrambi i modelli di Repository e Unit of Work, è necessario verificare se queste implementazioni serviranno o se è necessario crearne di proprie.

Ho scelto di creare il mio repository, seguendo il modello DDD: uno per radice aggregata. I motivi: incapsulare il codice di query, per evitare che si diffonda nel livello dell'applicazione e per essere in grado di simulare più facilmente durante il test dei controller dell'applicazione. Ho creato un repository generico basato su IRepository<TEntity>. Ci sono molti esempi là fuori. Ho trovato questo uno buono: http://architects.dzone.com/articles/implementing-repository

D'altro canto ho deciso di abbandonare il servizio IUnitOfWork, optando invece per l'implementazione predefinita. Tuttavia, ho creato un'astrazione IDbContext (non so perché Microsoft non l'abbia fatto da sé), così da poter prendere in giro DbContext durante il test dei servizi di repository.

Ho dato a IDbContext solo i membri di DbContext che volevo usare nel repository.Quindi:

public interface IDbContext: IDisposable 
{ 
    Database Database { get; } 
    DbEntityEntry Entry(object entity); 
    IDbSet<TEntity> Set<TEntity>() where TEntity : class; 
    int SaveChanges(); 
} 

Poi ho creato una struttura di Windsor e di installazione per il mio IDbContext e servizi IRepository:

public class EntityFrameworkFacility: AbstractFacility 
{ 
    protected override void Init() 
    { 
     Kernel.Register(Component.For<IDbContext>() 
           .ImplementedBy<MyEntities>() 
           .LifestylePerWebRequest(), 
         Component.For(typeof(IRepository<>)) 
           .ImplementedBy(typeof(Repository<>)) 
           .LifestylePerWebRequest()); 
    } 
} 

public class PersistenceInstaller : IWindsorInstaller 
{ 
    public void Install(IWindsorContainer container, IConfigurationStore store) 
    { 
     container.AddFacility<EntityFrameworkFacility>(); 
    } 
} 

Il pezzo finale è stato quello di estendere la classe contesto Entity Framework per implementare IDbContext, e ombra la set() per tornare IDbSet piuttosto che DbSet:

public partial class MyEntities : IDbContext 
{ 
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class 
    { 
     return base.Set<TEntity>(); 
    } 
} 

Con tutto questo in posto (e la registrazione ControllerFactory illustrato nella documentazione di Windsor), è diventa banale per ottenere Windsor iniettare oggetti IRepository (o IDbContext) in costruttori controllore, come richiesto:

public ControllerBase(IRepository<Contact> repo) 
{ 
    _repo = repo; 
} 

Nei test di unità Repository, un vero esempio repository può essere sostenuta con un IDbContext finto:

mocks = new MockRepository(); 
context = mocks.StrictMock<IDbContext>(); 
repo = new Repository<Contact>(context); 

Nei test unità di controllo, un repository finto può essere utilizzato:

mocks = new MockRepository(); 
repo = mocks.StrictMock<IRepository<Contact>>(); 
ContactController controller = new ContactController(repo); 
+2

Grazie per aver dedicato del tempo per fornire un codice di esempio e riassumere :-) –

+1

Una piccola correzione, il tuo MyEntities ha bisogno di estendere DbContext. – SoWeLie

+0

In realtà, la classe di contesto generata da Entity Framework eredita da DbContext - questa è una classe parziale che estende quella classe per supportare anche IDbContext. –

Problemi correlati