2010-03-13 11 views
6

Con qualche aiuto gentile da StackOverflow, ho Unità Framework per creare le mie dipendenze incatenati, tra cui un Entity Framework DataContext oggetto:Unità quadro - la creazione e lo smaltimento datacontexts Entity Framework al momento opportuno

using (IUnityContainer container = new UnityContainer()) 
{ 
    container.RegisterType<IMeterView, Meter>(); 
    container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(new ContainerControlledLifetimeManager()); 
    container.RegisterType<IRepositoryFactory, SQLiteRepositoryFactory>(); 
    container.RegisterType<IRepositoryFactory, WCFRepositoryFactory>("Uploader"); 
    container.Configure<InjectedMembers>() 
     .ConfigureInjectionFor<CommunergySQLiteEntities>(
      new InjectionConstructor(connectionString)); 

    MeterPresenter meterPresenter = container.Resolve<MeterPresenter>(); 

questo funziona davvero bene nella creazione del mio oggetto Presenter e visualizzando la vista correlata, sono davvero contento.

Tuttavia, il problema in cui mi sto imbattendo ora è il momento della creazione e dell'eliminazione dell'oggetto Entity Framework (e ho il sospetto che ciò avvenga per qualsiasi oggetto IDisposable). Con Unity come questo, l'oggetto "CommunergySQLiteEntities" SQL EF è creato subito, come ho aggiunto la sua interfaccia, IUnitOfWork al costruttore della MeterPresenter

public MeterPresenter(IMeterView view, IUnitOfWork unitOfWork, IRepositoryFactory cacheRepository) 
    { 
     this.mView = view; 
     this.unitOfWork = unitOfWork; 
     this.cacheRepository = cacheRepository; 
     this.Initialize(); 
    } 

mi sentivo un po 'a disagio su questo, al momento, dato che non voglio mantenere aperta una connessione al database, ma non ho potuto vedere in nessun altro modo usando l'iniezione della dipendenza Unity. Abbastanza sicuro, quando in realtà cerco di usare il DataContext, ottengo questo errore:

((System.Data.Objects.ObjectContext)(unitOfWork)).Connection 
    '((System.Data.Objects.ObjectContext)(unitOfWork)).Connection' 
threw an exception of type 'System.ObjectDisposedException' 
System.Data.Common.DbConnection {System.ObjectDisposedException} 

La mia comprensione del principio di IoC è che si imposta tutte le dipendenze in alto, risolvere il vostro oggetto e si va via . Tuttavia, in questo caso, alcuni degli oggetti figlio, ad esempio il datacontext, non devono essere inizializzati nel momento in cui viene creato l'oggetto padre Presenter (come si farebbe passandoli nel costruttore), ma il Presenter deve sapere quale tipo utilizzare per IUnitOfWork quando vuole parlare con il database.

Idealmente, voglio qualcosa di simile dentro il mio presentatore ha deliberato:

using(IUnitOfWork unitOfWork = new NewInstanceInjectedUnitOfWorkType()) 
{ 
    //do unitOfWork stuff 
} 

Così il presentatore sa cosa IUnitOfWork applicazione da utilizzare per creare e smaltire subito, preferibilmente dalla chiamata RegisterType originale. Devo mettere un altro contenitore Unity all'interno del mio Presenter, a rischio di creare una nuova dipendenza?

Questo è probabilmente molto ovvio per un guru IoC, ma apprezzerei davvero un puntatore nella giusta direzione.

risposta

4

Non è necessario preoccuparsi di inizializzare un ObjectContext allo stesso tempo che è stato creato il presentatore - un ObjectContext non regge aprire una connessione al database. Piuttosto, apre le connessioni quando necessario, quando in realtà ha bisogno di parlare al database, ad esempio quando si esegue una query o si commettono modifiche e si chiude nuovamente la connessione non appena è terminata. Tiene aperta la connessione solo se la apri esplicitamente, cosa molto rara da fare con Entity Framework.

Se si sta ottenendo uno ObjectDisposedException utilizzando ContainerControlledLifetimeManager, significa che il contenitore viene eliminato prima del presentatore, che è un errore di progettazione.Non è del tutto chiaro quale sia il tuo ambiente (ASP.NET? Winforms?), Ma lo ContainerControlledLifetimeManager probabilmente non è appropriato qui, poiché funziona come un'istanza Singleton. Normalmente si vorrà creare una nuova istanza di contesto quando si risolve il tipo: ci sono molti problemi che si possono verificare e si verificheranno se si usa invece un singleton.

Quindi, mi piacerebbe sbarazzarsi di ContainerControlledLifetimeManager qui e assicurarsi inoltre che il contenitore non venga smaltito troppo presto; il fatto che si trovi in ​​un blocco using indica che questa è probabilmente la causa del tuo ObjectDisposedException. (È comunque necessario disporre del contenitore, ovviamente - è solo che probabilmente stai facendo qualcosa come la creazione di un modulo non modale, che rimane attivo molto tempo dopo che il controllo lascia lo scope using).

+0

Ciao Aaron - tu e Michael avevate ragione entrambi, era quel piccolo giocherellone ContainerControlledLifetimeManager che avevo in giro da una versione precedente. L'ho rimosso, tornato ad avere IUnitOfWork nel costruttore di Presenter, e sembra abbastanza felice. grazie a tutti, molto istruttivo ... – TobyEvans

+0

e la ragione per cui l'avevo in primo luogo era questa domanda: http://stackoverflow.com/questions/2412563/unity-framework-reusing-instance Tuttavia, I 've ora è cambiato il mio disegno di utilizzare una fabbrica per creare i miei repository, e il metodo di fabbrica prende l'IUnitOfWork come parametro: var = localcache cacheRepository.CreateRealtimeRepository (unitOfWork) quindi non ho bisogno di avere la ContainerControlledLifetimeManager Come ho detto, molto educativo ... – TobyEvans

2

Perché non si rimuove il componente IUnitOfWork dal costruttore e viene invece iniettato il contenitore dell'unità? Quindi avrai la flessibilità di chiamare container.Resolve<IUnitOfWork>() ovunque nel tuo codice, quando appropriato.

Esempio:

using(IUnitOfWork unitOfWork = container.Resolve<IUnitOfWork>()) 
{ 
    //do unitOfWork stuff 
} 

Non dimenticare di impostare la durata dell'istanza al singolo.

Michael

+1

Icky. I componenti non dovrebbero dipendere dal contenitore, dovrebbero dipendere dalle loro dipendenze effettive. – Aaronaught

+0

a volte è utile iniettare il contenitore, è ancora possibile disaccoppiarlo;) –

+0

Infatti in alcuni progetti su cui ho lavorato avevamo una proprietà statica che rappresenta IUnityContainer che poteva essere consumata anche senza l'iniezione. –

0

So che questa domanda è vecchia, ma vorrei ancora dare i miei 2 centesimi qui.

È possibile registrare normalmente l'astrazione di UnitOfWork, ma richiedere una Func<IUnitOfWork> nella classe anziché un'istanza. Questa è veramente una caratteristica di Unity, in quanto è in grado di risolvere i delegati che creano il tuo oggetto invece di ottenere subito l'oggetto.

In questo modo, è possibile eseguire ciò che si desidera, ovvero controllare l'ambito dell'unità di lavoro all'interno del metodo.

In breve:

container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(); 

... 

public MeterPresenter(IMeterView view, Func<IUnitOfWork> unitOfWorkFactory, IRepositoryFactory cacheRepository) 
{ 
    this.mView = view; 
    this.unitOfWorkFactory = unitOfWorkFactory; 
    this.cacheRepository = cacheRepository; 
    this.Initialize(); 
} 

... 

using(IUnitOfWork unitOfWork = unitOfWorkFactory()) 
{ 
    //do unitOfWork stuff 
} 

Ho usato questo un paio di volte già e io personalmente consiglio, dal momento che avete ancora il pieno controllo su tutto, compreso il beffardo per test di unità, mentre ancora non è giunto il codice a tutto tranne che alle dipendenze.

È possibile creare un'interfaccia e iniettarla invece se è necessaria una logica più complessa, ma il delegato Func<T> è sufficiente nella maggior parte dei casi.