2010-03-13 11 views
6

Con un poco de ayuda bondadosa de stackoverflow, tengo Marco de la Unidad para crear mis dependencias encadenados, incluyendo un marco de la entidad objeto DataContext:marco de la Unidad - la creación y eliminación datacontexts de Entity Framework en el momento apropiado

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>(); 

este funciona muy bien al crear mi objeto Presenter y mostrar la vista relacionada. Estoy realmente contento.

Sin embargo, el problema con el que me estoy encontrando ahora es sobre el tiempo de creación y eliminación del objeto Entity Framework (y sospecho que esto irá para cualquier objeto IDisposable). El uso de la Unidad de esta manera, el objeto "CommunergySQLiteEntities" SQL EF se crea de inmediato, ya que he añadido su interfaz, IUnitOfWork al constructor de la MeterPresenter

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

me sentí un poco incómodo con esto en el momento, ya que no quiero mantener abierta una conexión de base de datos, pero no pude ver de otra manera el uso de la inyección de dependencia de Unity. Efectivamente, cuando en realidad intenta utilizar el DataContext, me sale este error:

((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} 

Mi comprensión del principio de la COI es que configure todas sus dependencias en la parte superior, resolver su objetivo y ya está . Sin embargo, en este caso, algunos de los objetos secundarios, por ejemplo, el contexto de datos, no necesitan inicializarse en el momento en que se crea el objeto padre Presenter (como lo haría al pasarlos en el constructor), pero el presentador necesita saber qué tipo usar para IUnitOfWork cuando quiere hablar con la base de datos.

Idealmente, quiero algo como esto dentro de mi Presentador resuelto:

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

Así que el presentador sabe lo IUnitOfWork aplicación a utilizar para crear y disponer de inmediato, preferiblemente de la llamada RegisterType originales. ¿Tengo que poner otro contenedor de Unity dentro de mi Presentador, a riesgo de crear una nueva dependencia?

Esto es probablemente muy obvio para un gurú de IoC, pero realmente agradecería un apuntador en la dirección correcta.

Respuesta

4

Usted no necesita preocuparse por la inicialización de un ObjectContext, al mismo tiempo que se crea el presentador - un ObjectContext no mantener abierta una conexión de base de datos. Más bien, abre las conexiones según sea necesario, cuando realmente necesita hablar con la base de datos, es decir, cuando ejecuta una consulta o confirma los cambios, y cierra la conexión nuevamente tan pronto como termina. Solo mantendrá la conexión abierta si la abre explícitamente, lo cual es algo raro de hacer con Entity Framework.

Si obtiene un ObjectDisposedException usando el ContainerControlledLifetimeManager, significa que su contenedor está siendo eliminado antes del presentador, lo que es un error de diseño.No está del todo claro cuál es su entorno (ASP.NET? Winforms?), Pero el ContainerControlledLifetimeManager probablemente no sea apropiado aquí, ya que funciona como una instancia de Singleton. Normalmente, en realidad querrás crear una nueva instancia de contexto cuando resuelvas el tipo; existen muchos problemas que puedes y te toparán si usas un singleton en su lugar.

Por lo tanto, me gustaría deshacerme de la ContainerControlledLifetimeManager aquí, y también me aseguro de que su contenedor no se elimine demasiado pronto; el hecho de que está en un bloque using indica que esta es probablemente la causa de su ObjectDisposedException. (Por supuesto, todavía tiene que deshacerse del contenedor; es solo que probablemente esté haciendo algo así como crear una forma no modal, que permanece activa mucho después de que el control abandone el alcance using).

+0

Hola Aaron: Michael y tú tenéis razón, fue ese pequeño y travieso ContainerControlledLifetimeManager que tuve de una versión anterior. Lo eliminé, volví a tener IUnitOfWork en el constructor de Presenter, y parece bastante feliz. gracias a todos, muy educativo ... – TobyEvans

+0

y la razón por la que tuve que en primer lugar fue esta pregunta: http://stackoverflow.com/questions/2412563/unity-framework-reusing-instance Sin embargo, yo 've ahora cambiado mi diseño de utilizar una fábrica para crear mis repositorios, y el método de fábrica toma el IUnitOfWork como el parámetro: var = LocalCache cacheRepository.CreateRealtimeRepository (UnitOfWork) así que no es necesario tener la ContainerControlledLifetimeManager Como dije, muy educativo ... – TobyEvans

2

¿Por qué no acaba de eliminar el IUnitOfWork del constructor y en su lugar, inyectar el contenedor de unidad? Por lo tanto, tendrá la flexibilidad de llamar al container.Resolve<IUnitOfWork>() en cualquier lugar de su código, según corresponda.

Ejemplo:

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

No se olvide de establecer el tiempo de vida de la instancia a single.

Michael

+1

Icky. Los componentes no deben depender del contenedor, sino que deben depender de sus dependencias reales. – Aaronaught

+0

Algunas veces es útil inyectar el contenedor, todavía tiene desacoplamiento;) –

+0

De hecho, en algunos proyectos en los que he trabajado teníamos una propiedad estática que representaba el IUnityContainer que podría consumirse incluso sin inyección. –

0

Sé que esta pregunta es antigua, pero me gustaría dar mis 2 centavos aquí.

Puede registrar su abstracción de UnitOfWork normalmente, pero solicite un Func<IUnitOfWork> en su clase en lugar de una instancia. Esta es una característica clara de Unity en realidad, pudiendo resolver delegados que crean su objeto en lugar de obtener el objeto de inmediato.

De esta manera, puede hacer lo que quiera, es decir, controlar el alcance de la unidad de trabajo dentro de su método.

En resumen:

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 
} 

He utilizado este un par de veces ya y yo personalmente recomiendo, ya que todavía tiene el control total sobre todas las cosas, incluyendo burla para las pruebas unitarias, mientras que todavía no acoplando su código a todo menos a sus dependencias.

Puede crear una interfaz IUnitOfWorkFactory e insertarla en su lugar si necesita una lógica más compleja, pero el delegado Func<T> es suficiente en la mayoría de los casos.

Cuestiones relacionadas