El respositorio genérico.Un Derrochador en épocas de crisis.
Durante muchos días,semanas,meses e incluso años he visto la siguiente definición de un repositorio.
1. Interface IRepository.
1: public interface IRepository<T> where T:class
2: {
3: T Get(int Id);
4: T Insert(T Entity);
5: void Update(T Entity);
6: void Delete(T Entity);
7: IEnumerable<T> GetAll();
8:
9: IUnitOfWork UnifOfWork { get;}
10: }
2. Interface IUnitOfWork
1: public interface IUnitOfWork
2: {
3: void Commit();
4: void RollBack();
5: }
3. Interface IContext que implementamos en nuestro contexto de Entity Framework, en ella simplemente voy a definir un método que me devuelva un DbSet<T>
1: public interface IContext:IUnitOfWork
2: {
3: DbSet<TEntity> CreateSet<TEntity>() where TEntity : class;
4: }
El objetivo de este post no es demostrar que evidentemente estamos ligados fuertemente a EF, desde el momento que utilizamos DbSet<T>, sino analizar la interface IRepository.
Para ello voy a crear una clase base o Layer Supertype donde voy a implementar la interface IRepository
1: public class Repository<T> : IRepository<T> where T:class
2: {
3: readonly IContext _Context;
4: readonly IUnitOfWork _UnitOfWork;
5: public Repository(IContext Context)
6: {
7: if (Context == null)
8: throw new NullReferenceException("El contexto no puede ser nulo");
9:
10: _Context = Context;
11: _UnitOfWork = Context;
12: }
13: public T Get(int Id)
14: {
15: return GetSet().Find(Id);
16: }
17:
18: public T Insert(T Entity)
19: {
20: throw new NotImplementedException();
21: }
22:
23: public void Update(T Entity)
24: {
25: throw new NotImplementedException();
26: }
27:
28: public void Delete(T Entity)
29: {
30: throw new NotImplementedException();
31: }
32:
33: public IEnumerable<T> GetAll()
34: {
35: return GetSet().AsEnumerable();
36: }
37:
38: public IUnitOfWork UnifOfWork
39: {
40: get { return _UnitOfWork; }
41: }
42: private DbSet<T> GetSet()
43: {
44: return _Context.CreateSet<T>();
45: }
46: }
Como podéis observar los métodos Insert,Update y Delete no están implementados, puesto que como dije anteriormente no es objetivo de este post y aparte tampoco me apetece:).
Vamos a realizar un análisis de esto.
1. Estamos ligados a EF sí y no más comentarios:).
2.La interface IRepository<T> expone métodos que con total seguridad no necesitemos? Sí.
Un ejemplo sencillo la entidad X no se puede modificar ni eliminar. Para ello os recomiendo esta lectura que ayer concretamente twitee.
https://twitter.com/_PedroHurtado/status/210311759872000000
3. Si vemos estas pegas y lo seguimos utilizando, porque no le damos una vuelta de tuerca. Para ello os voy a exponer el siguiente requisito y lógicamente no voy a escribir todas las entidades necesarias sino quiero que os lo imaginéis:).
Un Cliente tiene una forma de pago asignada por defecto, para que cuando cree una nueva factura se la asigne a esta. Con no otro objetivo que ayudar al usuario y este lógicamente podrá cambiarla por cada operación, entendido no?
Tal y como hemos creado nuestro repositorio no nos queda otra opción que leer todos los datos del cliente y no son tres campos en la vida real. Para obtener el Id y la Descripción de la forma de pago tenemos que leer todos los datos del cliente y además hacer otra lectura para recuperar el nombre de la forma de pago. Luego nos quejamos que EF es lento o somos nosotros los que lo hacemos lento.
Porque no exponemos nuestro método Get con la siguiente firma
TResult Get<TResult>(int Id,Expression<Func<T,TResult>> Select);
Lo mismo ahora podemos implementar nuestro Get de esta forma
1: public TResult Get<TResult>(int Id, Expression<Func<T, TResult>> Select)
2: {
3: return GetSet()
4: .Where(c => c.Id == Id)
5: .Select(Select)
6: .FirstOrDefault();
7: }
y definir un repositorio para la clase Cliente que nos devuelva bien la Entidad Cliente o parte de ella, queréis verlo?
Bueno pues escribimos las clases necesarias para ello y veréis:)
1: public class Entity
2: {
3: public int Id { get; set; }
4: }
5: public class Cliente:Entity
6: {
7: public string Nombre { get; set; }
8: public FormaPago FormaPago { get; set; }
9: }
10: public class FormaPago:Entity
11: {
12: public string Nombre { get; set; }
13: }
14: public interface IRepositoryCliente : IRepository<Cliente>
15: {
16:
17: }
18: public class RepositoryCliente : Repository<Cliente>, IRepositoryCliente
19: {
20: public RepositoryCliente(IContext Context)
21: : base(Context)
22: {
23: }
24: }
25:
26: public class Contexto : DbContext, IContext
27: {
28:
29: public DbSet<TEntity> CreateSet<TEntity>() where TEntity : class
30: {
31: throw new NotImplementedException();
32: }
33:
34: public void Commit()
35: {
36: throw new NotImplementedException();
37: }
38:
39: public void RollBack()
40: {
41: throw new NotImplementedException();
42: }
43: }
Mirad lo que puedo hacer, ooooo!!!
1: public class Test
2: {
3: public Test()
4: {
5: using (Contexto ct = new Contexto())
6: {
7: var idCliente =10;
8: var repositorio = new RepositoryCliente(ct);
9: //Devolver un cliente
10: var cliente = repositorio.Get(idCliente, c => c);
11: //Devolver partes de un cliente
12: var formapago = repositorio.Get(idCliente, c => new { Id = c.FormaPago.Id, Nombre = c.FormaPago.Nombre });
13:
14: }
15: }
16: }
Pero lo que más me gusta es esto.

Pedro la verdad que eres un sacrílego, no se cuantos principios has roto y cuantos antipatrones has implementado, además un método que devuelve un Tipo Anónimo.
Yo me contesto. Si lo que tu me digas, pero yo necesito el Id y el Nombre de la Forma de Pago no doscientos campos que tiene el Cliente. Anda derrochador,malgastoso no vas a tener ni un duro cuando seas mayor:).
Conclusiones.
Porque no nos acostumbramos a leer exclusivamente lo que necesitamos y nos dejamos de películas, creo que la vida nos irá mejor. Una última reflexión, ¿me habré cargado AutoMapper?. Otro malgastoso, o estoy en el camino:).