2010-07-31 9 views
12

Estoy buscando utilizar el patrón IRepository (respaldado por NHibernate, si es que importa) en un proyecto pequeño. El dominio es simple, intencionalmente para permitirme concentrarme en la comprensión del patrón IRepository. La clase de dominio solitario es Movie, con propiedades para Year, Genre y Title. Mi intención sería "obtener" películas cuyas propiedades coincidan con los criterios de los tipos mencionados anteriormente.¿Estoy utilizando IRepository correctamente?

Convención parece ser la de tener un IRepository interfaz genérica, similar a la siguiente:

public interface IRepository<T> 
{ 
    T Get(int id); 
    T[] GetAll(); 
    void Add(T item); 
    void Update(T item); 
    void Delete(T item); 
} 

Con una aplicación de base:

public abstract class Repository<T> : IRepository<T> 
{ 
    public T Get(int id) { ... } 
    public T[] GetAll() { ... } 
    public void Add(T item) { ... } 
    public void Update(T item) { ... } 
    public void Delete(T item) { ... } 
} 

Luego de tener una interfaz de dominio específico:

public interface IMovieRepository 
{ 
    Movie[] GetByGenre(Genre genre); 
    Movie[] GetByYear(int year); 
    Movie[] GetByTitle(string title); 
} 

Con una implementación que también extiende la base Repository clase:

public class MovieRepository : Repository<Movie>, IMovieRepository 
{ 
    public Movie[] GetByGenre(Genre genre) { ... } 
    public Movie[] GetByYear(int year) { ... } 
    public Movie[] GetByTitle(string title) { ... } 
} 

que tendría que añadir la necesaria adaptación a la clase base, así como el concreto, el uso de NHibernate, pero me gustaría saber si estoy en el camino correcto con esta configuración.

Parece que hay un poco de sobrecarga para una sola clase de dominio, aunque sería menos notable si hubiera varias clases de dominio involucradas. En este momento estoy tratando de mantenerlo simple para que pueda precisar el concepto.

Respuesta

2

yo diría, que está cerca del repositorio que yo uso en la producción de una solución de planificación de recursos en las empresas de transporte (utilizando NHibernate también) - por lo que para empezar está a la derecha camino en mi opinión.Estoy de acuerdo con dbones en el uso de IEnumerables/IList en lugar de matrices; terminará escribiendo .ToArray() varias veces :-).

Un par de cosas que podría considerar: Composición

favor sobre la herencia - en lugar de heredar desde el repositorio abstracto - deja que sea no abstracta e inyectarlo en el 'Héctor y delegar las llamadas - esto hace que su diseño más robusto en ciertas situaciones (por ejemplo, para un repositorio de solo consultas, etc.) De esa manera también tiene la opción de permitir que el repositorio abstracto sea instanciable (¿es una palabra?) y controlar si se debe compartir en todos los repositorios.

seguimiento a ese punto - es posible que desee cambiar el repositorio de base de disponer de métodos genéricos en lugar de heredar de la interfaz genérica:

public class Repository 
{ 
    public void Add<T>(T entity) 
    { 
     using(var session = GetSession()) 
     using(var tx = session.BeginTransaction()) 
     { 
      session.Save(entity) 
      //Transaction handling etc. 
     } 
    } 
    .... //repeat ad nasseum :-) 
} 

Es posible que desee dejar que los repositorios específicos tienen acceso a los ISession - esto mejora en gran medida la flexibilidad con la que puede realizar sus consultas y controlar la búsqueda ansiosa/diferida y obtiene el máximo provecho de NHibernate, etc.

public class Repository 
{ 
    public IList<T> WrapQueryInSession<T>(Func<ISession,IList<T> query) 
    { 
     using(var session = GetSession()) 
     using(var tx = session.BeginTransaction()) 
     { 
      var items = query(session); 
      //Handle exceptions transacitons etc. 
      return items; 
     } 
    } 
} 

Uso:

public class MovieRepository : IMovieRepository 
{ 
    private Repository _repository; 
    public MovieRepository(Repository repository) 
    { 
     _repository = repository; 
    } 
    public IList<Movie> GetByYear(int year) 
    { 
     Func<ISession, IList<Movie> query = session => 
     { 
      var query = session.CreateQuery("from Movie"); //or 
      var query = session.CreateCriteria("from Movie"); //or 
      var query = session.Linq<Movie>(); 
      //set criteria etc. 
      return query.List<Movie>(); //ToList<Movie>() if you're using Linq2NHibernate 
     }: 
     return _repository.WrapQueryInSession(query); 
    } 
} 

Usted también puede fijar un valor de retorno bool en sus métodos si algo va mal - y tal vez una salida IEnumerable de los errores que tengan sentido en el código de llamada .

Pero, en general, estos son solo mis cositas que he agregado a lo largo del tiempo para cumplir mejor con mi uso, y son totalmente opcionales, solo para pensar :-). Creo que estás en el camino correcto, no veo ningún problema importante en tu código.

la esperanza que esto tiene sentido :-)

+0

Me gusta su sugerencia sobre el uso de un 'Repository' no abstracto para manejar el trabajo de bajo nivel de tipo CRUD, delegado de los repositorios específicos. Sí plantea el problema de obtener una 'ISession' tanto en el repos genérico como en el repositorio específico que lo usa. –

+0

La ISession se debe obtener a través de una ISessionFactory privada en el repositorio. El Repositorio específico luego usa un método WrapxxxInSession del Repositorio genérico. – Goblin

+0

Ah sí, eso ayuda. Me gusta bastante la idea general ... otro beneficio es que el IRepository inyectado se puede burlar para probarlo. Habrá el truco de construir y pasar objetos en el orden correcto, pero es probable que una herramienta de IoC pueda manejar eso. –

6
  1. intenta no devolver el código array. use IEnumerable<T>, ICollection<T> o IList<T>, esto relacionará aún más su código.

  2. interfaz de IMovieRepository. este repositorio incluye el CRUD. por lo tanto, hacen que sea

IMovieRepository : IRepository<Movie> {}

Esto no va a cambiar su clase MovieRepository como que implementar la interfaz correctamente. le permitirá desacoplar sus clases si desea cambiar la implementación en una fecha posterior.

finalmente. esto está bien para uno de los métodos. como tiene una funcionalidad especializada, ha especializado el repositorio para que se adapte.

hay otras formas, que le permiten utilizar 1 clase de reposición y pasar la consulta requerida. Esto se llama patrón de especificación. Hice un proyecto que utiliza esto ubicado en codeplex con informe http://whiteboardchat.codeplex.com

de otro modo sería tener un método para pasar los criterios. hay un proyecto de código abierto llamado Sharp Architecture, que creo que tiene esta codificación.

Esperanza esto ayuda

+0

Gracias por los indicadores sobre la implementación del patrón de especificación y los métodos basados ​​en criterios. Ambas son ideas interesantes, y las miraré más de cerca, aunque ahora estoy manteniendo las cosas simples y directas. –

0

Como alimento para el pensamiento, si su ORM de elección tiene un proveedor de LINQ (y NH tiene uno), puede intentar el repositorio consultable que es bastante similar a una colección:

public interface IRepository<T> : ICollection<T>, IQueryable<T> 

he escrito alguno sobre ello en mi sitio: Repository or DAO?: Repository tiene similitudes con el de la construcción (sólo porque una colección apoya CRUD también), el enfoque yo intento significa que usted puede tener código que no es necesariamente consciente de tratar con un repositorio como se puede programar contra el ICollection o IQueryable interfaz ...

Cuestiones relacionadas