2012-04-03 16 views
5

Ho la seguente repository ADO NetIDisposable su un repository iniettato

public class Repository : IRepository, IDisposable 
{ 
    private readonly IUnitOfWork UnitOfWork; 
    private SqlConnection Connection; 

    public Repository(IUnitOfWork unitOfWork, connectionString) 
    { 
     UnitOfWork = unitOfWork; 
     Connection = new SqlConnection(connectionString); 
     Connection.Open(); 
    } 

    public MyObject FindBy(string userName) 
    { 
     //...Ado .Net command.ExecuteReader, etc. 
    } 
} 

Questo repository viene iniettato con un contenitore CIO ad un Servizio di dominio e viene utilizzato in questo modo:

public class UserDomainService : IUserDomainService 
{ 
    private readonly IRepository Repository; 

    public UserDomainService(IRepository repository) 
    { 
     Repository = repository; 
    } 

    public User CreateNewUser(User user) 
    { 
     using(Repository) 
     { 
     var user = Repository.FindBy(user.UserName); 
     if(user != null) 
      throw new Exception("User name already exists!"); 

     Repository.Add(user); 
     Repository.Commit(); 
     } 
    } 
} 

L'idea è che metto sempre l'oggetto Repository in un'istruzione using così quando termina, la connessione viene chiusa e smaltita, ma la vedo come un problema poiché la classe Domain Service è ancora viva e se c'è una seconda chiamata ad essa, fallirà poiché il repository è già stato distrutto.

Ora ho il controllo completo di tutto il codice e desidero progettare solo chiamate di servizio a grana grossa, ma c'è qualcosa in tutto ciò che non sembra giusto.

Lo sto facendo in questo modo, così posso evitare che il servizio di dominio conosca i metodi OpenConnection e CloseConnection nel repository.

Questo design è intrinsecamente negativo o esiste un modo migliore per farlo?

Dopo pensiero: Tutto l'albero delle dipendenze viene generata a livello di WCF, quando arriva una richiesta e, naturalmente, si può vedere che la connessione viene aperta in quel momento in quanto accade nel costruttore del repository così credo che non è poi così male visto che è aperto solo per la durata di questa particolare chiamata. Ho ragione su questa ipotesi o sto facendo qualcosa di terribilmente cattivo aprendo la connessione DB così presto nel processo?

+0

È 'IRepository' strettamente accoppiato a' Repository'? Significato, contiene i metodi di esso, come ad esempio 'Trova'? In tal caso, tale interfaccia implica 'IDisposable'? –

+0

Ho una mia domanda che potrebbe riguardare questo: [ServiceContainer, IoC e oggetti usa e getta] (http://stackoverflow.com/questions/556580/servicecontainer-ioc-and-disposable-objects). –

+1

Perché hai bisogno di 'SqlConnection' nel' Repository'? Sembra più qualcosa per il tuo 'IUnitOfWork'. – Steven

risposta

8

Inietta un factory che crea le istanze necessarie, non un'istanza stessa.

assumere un IRepositoryFactory in modo da poter creare un IRepository e smaltire ogni volta che si utilizza. In questo modo, né il servizio di dominio né la fabbrica avrebbero bisogno di essere usa e getta. Inoltre, e soprattutto, si mantiene il codice astratto continuando a iniettare l'implementazione anziché crearla.

public class UserDomainService : IUserDomainService 
{ 
    private readonly IRepositoryFactory RepositoryFactory; 

    public UserDomainService(IRepositoryFactory factory) 
    { 
     RepositoryFactory = factory; 
    } 

    public User CreateNewUser(User user) 
    { 
     using (IRepository repository = RepositoryFactory.Create()) 
     { 
     var user = repository.FindBy(user.UserName); 
     if(user != null) 
      throw new Exception("User name already exists!"); 

     repository.Add(user); 
     repository.Commit(); 
     } 
    } 
} 

Non è sempre necessario iniettare il tipo necessario. Leggendo Castle Windsor (il cui mindset è register-resolve-release), si scopre che se si desidera risolvere le cose a un tempo indeterminato nella vita dell'app, si consiglia di utilizzare le Factory di tipo.

Sapete che avrete bisogno di un deposito, ma non so quando. Invece di chiedere un repository, chiedere qualcosa che crei. Il livello di astrazione viene quindi mantenuto e non è stata rilevata alcuna implementazione.

+0

DUH! Non so perché non ci ho pensato. Grazie. –

+0

@SergioRomero A volte devi solo fare un passo indietro e far emergere il problema con altre persone prima di giungere alla conclusione corretta da solo. Lo faccio sempre, è molto facile rimanere intrappolato in cerchi di design se ci si infila tutto il tempo :-( –

+0

Ora si ha un'astrazione che perde. Violazione dell'incapsulamento. Il servizio non ha bisogno di sapere sulla durata del repository Hai rivelato il segreto.La soluzione migliore sarebbe riscrivere il repository in modo che abbia esso stesso aperto e chiuso la connessione per ogni transazione –

1

Il problema che si ha è di proprietà. La classe UserDomainService non crea il numero IRepository, tuttavia assume ancora la proprietà di tale istanza, poiché la elimina.

La regola generale è che colui che crea un oggetto dovrebbe distri- buirlo. In altre parole, colui che crea l'oggetto è il proprietario e il proprietario distruggerà quell'oggetto.

Esistono due soluzioni al problema.

  1. Creare un IRepositoryFactory, come Adam spiega chiaramente.Un metodo CreateNewRepository() su un factory di questo tipo comunica chiaramente che il chiamante ottiene la proprietà e deve disporre del repository creato.

  2. Lascia che colui che crea (e inietta) quel repository si occupi della eliminazione di quel repository. O lo fai manualmente nel tuo servizio WCF o usi un framework IoC/DI. Nel caso in cui si utilizzi un framework DI, probabilmente si dovrebbe considerare una durata di Per Web Request o qualcosa di simile.

Ultima nota, i tuoi attrezzi IRepositoryIDisposable. Quando si sceglie la soluzione 2, è possibile rimuovere l'interfaccia IDisposable da IRepository, che nasconde il fatto che le risorse sono coinvolte dall'applicazione. Nascondere IDisposable dall'applicazione è una buona cosa, dal momento che quell'interfaccia è un'astrazione che perde. Si è già verificato questo, dal momento che chiamando Dispose all'interno dell'applicazione, si interrompe l'intera applicazione.

Problemi correlati