2009-07-23 7 views
7

Soy bastante nuevo en MVP y en el mundo de Entity Framework, así que tengan paciencia conmigo.EF ObjectContext, Service and Repository - Gestión de la duración del contexto.

Actualmente tengo una combinación de Vista + Presentador, la vista tiene dos eventos Editar y Eliminar y el presentador solo escucha estos eventos. También tengo un objeto de servicio y repositorios configurados. La capa de servicio toma algunas implementaciones del repositorio que tienen un ObjectContext por lo que el orden de la construcción está (pasando el objeto arriba a la de abajo:

ObjectContext 
    | 
    V 
Repositries 
    | 
    V 
Service Object 
    | 
    V 
Presenter 

Ahora el problema es que cuando creo contexto de objeto en la parte superior, Está activo durante todo el tiempo que el presentador está vivo, lo que significa que Editar y Eliminar utiliza la misma instancia de contexto del servicio.

Por lo tanto, llamar a ServiceObject.Delete y ServiceObject.Edit utiliza el mismo contexto, lo que dificulta administrar el seguimiento de cambios. Por lo que entiendo, el contexto en realidad solo debe ser de corta duración y solo para una unidad de trabajo, para mí, Editar y Eliminar son ambos lotes de trabajo diferentes.

¿Cómo se hace DI con el marco de entidad y todavía se administra el tiempo de vida del contexto?

He visto gente que acaba de actualizar el contexto del objeto en el lado del repositorio, este es un buen patrón.

o debería hacerlo en el objeto de servicio, algo así como:

ServiceObject{ 
    public void Edit(// some args) { 
    Using(var context = new MyObjectContext) { 
     var repo = new MyRepo(context); 
     var entity = repo.GetForID(12); 
     // Do some stuff for edit 
     context.SaveChanges(); 
    } 
    } 
} 

pero si lo hago como que ya no estoy pasando mi repositorio en el constructor de ServiceObject y no hacer DI :(.

¿Qué puedo hacer en esta situación?

¿alguien sabe cualquier proyecto de código abierto que puedo mirar que me puede ayudar con este problema.

Gracias.

Respuesta

24

Iré desde arriba (presentador) y describiré las relaciones entre los participantes.

El presentador obtiene el objeto de servicio a través de la dependencia. La funcionalidad del servicio se describe mediante su contrato:

class Presenter 
{ 
    public Presenter(IService service) 
    { 
    ... 
    } 
} 

La implementación del servicio se abstrae de la implementación de la capa de acceso a datos en particular. Básicamente, cuando el servicio realiza alguna acción que requiere interacción con el origen de datos, crea una instancia de unidad de trabajo y la elimina cuando termina.

interface IService 
{ 
    void Do(); 
} 

class Service : IService 
{ 
    private readonly IUnitOfWorkFactory unitOfWorkFactory; 
    public Service(IUnitOfWorkFactory unitOfWorkFactory) 
    { 
    this.unitOfWorkFactory = unitOfWorkFactory; 
    } 

    public void Do() 
    { 
    // Whenever we need to perform some data manipulation we create and later dispose 
    // dispose unit of work abstraction. It is created through a factory to avoid 
    // dependency on particular implementation. 
    using(IUnitOfWork unitOfWork = this.unitOfWorkFactory.Create()) 
    { 
     // Unit of work holds Entity Framework ObjectContext and thus it used 
     // create repositories and propagate them this ObjectContext to work with 
     IRepository repository = unitOfWork.Create<IRepository>(); 
     repository.DoSomethingInDataSource(); 

     // When we are done changes must be commited which basically means committing 
     // changes of the underlying object context. 
     unitOfWork.Commit(); 
    } 
    } 
} 


/// <summary> 
/// Represents factory of <see cref="IUnitOfWork"/> implementations. 
/// </summary> 
public interface IUnitOfWorkFactory 
{ 
    /// <summary> 
    /// Creates <see cref="IUnitOfWork"/> implementation instance. 
    /// </summary> 
    /// <returns>Created <see cref="IUnitOfWork"/> instance.</returns> 
    IUnitOfWork Create(); 
} 

/// <summary> 
/// Maintains a list of objects affected by a business transaction and coordinates the writing out of 
/// changes and the resolution of concurrency problems. 
/// </summary> 
public interface IUnitOfWork : IDisposable 
{ 
    /// <summary> 
    /// Creates and initializes repository of the specified type. 
    /// </summary> 
    /// <typeparam name="TRepository">Type of repository to create.</typeparam> 
    /// <returns>Created instance of the repository.</returns> 
    /// <remarks> 
    /// Created repositories must not be cached for future use because once this 
    /// <see cref="IUnitOfWork"/> is disposed they won't be able to work properly. 
    /// </remarks> 
    TRepository Create<TRepository>(); 

    /// <summary> 
    /// Commits changes made to this <see cref="IUnitOfWork"/>. 
    /// </summary> 
    void Commit(); 
} 

/// <summary> 
/// Represents factory of <see cref="UnitOfWork"/>s. 
/// </summary> 
public class UnitOfWorkFactory : IUnitOfWorkFactory 
{ 
    private readonly IUnityContainer container; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="UnitOfWorkFactory"/> class. 
    /// </summary> 
    /// <param name="container"> 
    /// Dependency injection container instance used to manage creation of repositories 
    /// and entity translators. 
    /// </param> 
    public UnitOfWorkFactory(IUnityContainer container) 
    { 
       this.conainer = container; 
    } 


    /// <summary> 
    /// Creates <see cref="IUnitOfWork"/> implementation instance. 
    /// </summary> 
    /// <returns>Created <see cref="IUnitOfWork"/> instance.</returns> 
    public IUnitOfWork Create() 
    { 
     var unitOfWork = this.container.Resolve<UnitOfWork>(); 
     unitOfWork.SetupObjectContext(); 
     return unitOfWork; 
    } 

    ... other members elidged for clarity 
} 

La implementación de IUnitOfWork recibe instancia de IUnityContainer y luego crea contenedor secundario y registra ObjectContext ejemplo allí. Este contenedor secundario se usará para crear repositorios y propagar ObjectContext.

Aquí es una aplicación simplificada de IUnitOfWork:

class UnitOfWork : IUnitOfWork 
{ 
    private readonly IUnityContainer container; 
    private ObjectContext objectContext; 

    public UnitOfWork (IUnityContainer container) 
    { 
    this.container = container.CreateChildContainer(); 
    } 

    public void SetupObjectContext() 
    { 
    this.objectContext = ... // Create object context here 
    this.container.RegisterInstance(context.GetType(), context); 
    } 

    public void Create<TRepository>() 
    { 
    // As long as we registered created object context instance in child container 
    // it will be available now to repositories during resolve 
    return this.container.Resolve<TRepository>(); 
    } 

    public void Commit() 
    { 
    this.objectContext.SaveChanges(); 
    } 
} 

class Repository : IRepository 
{ 
    private readonly SomeObjectContext objectContext; 

    public Repository(SomeObjectContext objectContext) 
    { 
    this.objectContext = objectContext; 
    } 

    public void DoSomethingInDataSource() 
    { 
    // You can use object context instance here to do the work 
    } 
} 
+1

Uuh eso es bueno! – Roubachof

Cuestiones relacionadas