2009-07-23 12 views
7

Sono abbastanza nuovo per l'MVP e il mondo di Entity Framework, quindi portami con me.EF ObjectContext, Service and Repository - Gestione della durata del contesto.

Attualmente ho una combinazione View + Presenter, la vista ha due eventi Modifica ed Elimina e il presentatore ascolta solo questi eventi. Ho anche un servizio oggetto e repository impostati. Lo strato servizio richiede alcune implementazioni repository che tengono un ObjectContext quindi l'ordine di costruzione è (passa nell'oggetto superiore a quello sottostante:

ObjectContext 
    | 
    V 
Repositries 
    | 
    V 
Service Object 
    | 
    V 
Presenter 

Ora il problema è che quando creo contesto dell'oggetto in alto, È vivo per tutto il tempo in cui il presentatore è vivo e significa che Modifica ed Elimina utilizzano la stessa istanza di contesto dal servizio

Quindi chiamare ServiceObject.Delete e ServiceObject.Edit utilizzano lo stesso contesto che rende difficile gestire il rilevamento delle modifiche. Da quello che ho capito il contesto dovrebbe essere solo di breve durata e solo per una unità di lavoro, per me Modifica ed Elimina sono entrambi diversi lotti di lavoro

Come si esegue la DI con il framework entità e si gestisce ancora la durata del contesto?

Ho visto persone appena rinnovate nel contesto dell'oggetto nel repository, è un buon esempio.

o dovrei farlo nell'oggetto di servizio, qualcosa di simile:

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

Ma se lo faccio come che non sono più passando il mio repository nel costruttore di ServiceObject e non facendo DI :(.

Cosa posso fare in questa situazione?

qualcuno sa qualsiasi progetto open source che posso guardare che può aiutare me con questo problema.

Grazie.

risposta

24

Andrò da capo (presentatore) e descriverò le relazioni tra i partecipanti.

Presenter ottiene oggetto di servizio tramite dipendenza. funzionalità del servizio è descritto con il suo contratto:

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

Servizio di attuazione si astrae dall'implementazione strato di accesso ai dati particolari. Fondamentalmente ogni volta che il servizio esegue un'azione che richiede l'interazione dell'origine dati, crea un'istanza di unità di lavoro e la elimina al termine.

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 
} 

L'attuazione di IUnitOfWork riceve istanza di IUnityContainer e quindi crea contenitore figlio e registra ObjectContext esempio ci. Questo contenitore secondario verrà utilizzato per creare repository e propagare ObjectContext.

Ecco un'implementazione semplificata di 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 questo è un bene! – Roubachof

Problemi correlati