views:

183

answers:

5

I'm developing a web application in ASP.NET MVC with NHibernate.

Based in articles and tutorials I've found at Google, I'm using Repository for my classes.

I have 10 classes and 10 repositories. Today I figured out that 90% of mine repositories are exactly equal each other, except for the class. This is one example:

public class PromocaoRepository:IPromocaoRepository {
    private ISession Session;

    public PromocaoRepository() {
        this.Session = NHibernateSessionFactory.OpenSession();
    }

    public void Add(Promocao promocao) {
        using(ITransaction transaction = this.Session.BeginTransaction()) {
            this.Session.Save(promocao);
            transaction.Commit();
        }
    }

    public void Edit(Promocao promocao) {
        using(ITransaction transaction = this.Session.BeginTransaction()) {
            this.Session.Update(promocao);
            transaction.Commit();
        }
    }

    public void Remove(Promocao promocao) {
        using(ITransaction transaction = this.Session.BeginTransaction()) {
            this.Session.Delete(promocao);
            transaction.Commit();
        }
    }

    public Promocao GetById(int id) {
        return this.Session.Get<Promocao>(id);
    }

}

There is a way to do a kind of generic repository witch I can use in all my classes?

If it's possible, what should I do in case I need to create a particular method for an specific class?

+3  A: 

You should make a generic repository, which you can use in the general case, and if any extra methods is needed for a particular class, add it by using inheritance. Using your example:

public class GenericRepository<TEntity> :IGenericRepository<TEntity> {
    private ISession Session;

    public GenericRepository() {
        this.Session = NHibernateSessionFactory.OpenSession();
    }

    public void Add(TEntity instance) {
        using(ITransaction transaction = this.Session.BeginTransaction()) {
            this.Session.Save(instance);
            transaction.Commit();
        }
    }

    /* other methods */ 
}

public class SpecificRepository : GenericRepository<SpecificEntity>, ISpecificRepository 
{
    public void SpecialQuery()  { /* added method implementation */ }
}
driis
Thanks! I just had to set my ISession Session as protected/public for the children repositories.
MCardinale
+5  A: 

From another thread:

public interface IRepository<T> : IQueryable<T>
{
  void Add(T entity);
  T Get(Guid id);
  void Remove(T entity);
}

public class Repository<T> : IQueryable<T>
{
  private readonly ISession session;

  public Repository(ISession session)
  {
    session = session;
  }

  public Type ElementType
  {
    get { return session.Query<T>().ElementType; }
  }

  public Expression Expression
  {
    get { return session.Query<T>().Expression; }
  }

  public IQueryProvider Provider
  {
    get { return session.Query<T>().Provider; } 
  }  

  public void Add(T entity)
  {
    session.Save(entity);
  }

  public T Get(Guid id)
  {
    return session.Get<T>(id);
  }

  IEnumerator IEnumerable.GetEnumerator()
  {
    return this.GetEnumerator();
  }

  public IEnumerator<T> GetEnumerator()
  {
    return session.Query<T>().GetEnumerator();
  }

  public void Remove(T entity)
  {
    session.Delete(entity);
  }   
}
Rafael Belliard
Never thought about making the Repository inherit IQueryable<T>. Any caveats to that?
mxmissile
It does not inherit but implement, IQueryable is an interface. And what it basically does is just forwarding calls to session.Query<T> which already has LINQ IQueryable implementation built-in.
gaearon
This is almost the exact code I use and I love it.
Ryan
This answer is very enlightening! I'll study all of it. Thank you very much.
MCardinale
+2  A: 

Have a look at my answer to the question "Asp.net MVC 2 Entity Framework Generic Repository Method. how to Update a specific Collumn" - it should give you a good idea of what to do.

HTHs, Charles

E.g.

Base model:

public interface IDbTable
{
    int Id { get; set; }
    DateTime DateCreated { get; set; }
    DateTime DateUpdated { get; set; }
}

public class DbTable
{
    public int Id { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
}

Your model

public class Category : DbTable
{
    public string Name { get; set; }
}

Your repository

public interface IBaseRepository<T> where T : class, IDbTable
{
    void Add<T>(T entity);
    void Edit<T>(T entity);
    void Remove<T>(T entity);
    T GetById(int id);
}

public class BaseRepository<T> : IBaseRepository<T>
{
    private ISession Session;

    public BaseRepository()
    {
        this.Session = NHibernateSessionFactory.OpenSession();
    }

    public void Add(T entity)
    {
        entity.DateCreated = DateTime.UtcNow;
        entity.DateUpdated = DateTime.UtcNow;

        using(ITransaction transaction = this.Session.BeginTransaction())
        {
            this.Session.Save(entity);
            transaction.Commit();
        }
    }

    public void Edit(T entity)
    {
        entity.DateUpdated = DateTime.UtcNow;

        using(ITransaction transaction = this.Session.BeginTransaction())
        {
            this.Session.Update(entity);
            transaction.Commit();
        }
    }

    public void Remove(T entity)
    {
        using(ITransaction transaction = this.Session.BeginTransaction())
        {
            this.Session.Delete(entity);
            transaction.Commit();
        }
    }

    public T GetById(int id)
    {
        return this.Session.Get<T>(id);
    }
}

Oh, and lets not forget the concrete implementation

public interface ICategoryRepository : IBaseRepository<Category>
{
    Category GetCategoryByName(string categoryName);
}

public CategoryRepository : BaseRepository<Category>
{
    public Category GetCategoryByName(string categoryName)
    {
        //blah
    }
}
Charlino
+2  A: 

Here is my answer to a similar question (28 votes as of right now):

http://stackoverflow.com/questions/1230571/advantage-of-creating-a-generic-repository-vs-specific-repository-for-each-objec/1231473#1231473

The idea is to genericize the implementation, not the interface. Instead of an outward-facing generic repository interface, create an inward-facing generic repository base class, which you use to easily implement entity-specific interfaces.

Edit: I should point out that generic repositories serve a very different function than specific repositories. Repositories are intended to encapsulate the data access mechanisms behind the entity's queries, including all of the query logic. A generic repository encapsulates the ability to create queries, but it doesn't encapsulate any specific query about an entity.

The point is to not make repository consumers responsible for writing their own queries. A generic repository lives at the same level of abstraction as an ORM; a specific repository lives at a level above that.

Bryan Watts