How to build a generic repository

2019-03-14 04:03发布

问题:

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?

回答1:

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 */ }
}


回答2:

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);
  }   
}
  • Wrapping up nHibernate in repositories
    That being said, you should take a look at S#arp Architecture, which already helps you with this repetitive process using Dependency Injection.


回答3:

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

Advantage of creating a generic repository vs. specific repository for each object?

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.



回答4:

Here's another example of a generic repository



回答5:

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
    }
}