Repository/Unit of Work pattern - How to query a repository for objects meeting certain criteria?
I have a standard repository interface in C# which includes the following methods:
IEnumerable<T> GetAll();
T GetById(int id);
void Delete(T entity);
void Add(T entity);
At my domain layer all I am instantiating is a new Unit of Work wrapper and passing it to a repository. The Unit of Work wrapper class hides whether I'm using NHibernate or Entity Framework and exposes a Commit() method.
In my domain layer, how can I query for objects which meet only 开发者_开发百科a particular criteria?
I think what I'm doing at the moment is terribly inefficient. I am currently doing this:
var results = myRepository.GetAll().Where......
If I have a very large amount of objects, is GetAll() going to return every one of them before filtering out the ones I don't need? How can I prevent unwanted object from being returned at all?
Obviously I could add more methods to the interface, but this doesn't seem in keeping with exposing only CRUD operations via the interface.
i.e. - I don't think I should be adding things like (but maybe I'm wrong):
IList<T> GetAllWhereMeetsMyCriteria();
Yes your GetAll().Where
will pull all your objects from database to your application and perform linq-to-objects. @Kamyar solution will do the same. @Dysaster has provided a correct solution while I was writing this answer.
First of all - do you really need repository which must support both NHibernate or EF? Is it customer requirement? If not you are wasting resources. Select the technology and create minimal abstraction needed for your application to follow separation of concerns.
If you really need high level abstraction and absolute independence on persistence API you should use the third pattern from the family - Specification pattern. With custom specifications you will be able to change persistence to anything if you translate your conditions described by specification into actions needed on data source. Criteria API in NHibernate or extension methods on IQueryable
are specifications but they are technology dependent.
Have a look at the repository pattern used in this blog post [weblogs.asp.net]. I have found the source code to employ a few interesting patterns that I keep returning back to and checking again. The code implements both repository and unit of work patterns.
To answer your specific question, the repository interface contains the method:
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
It is implemented as follows in the RepositoryBase<T>
class:
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
Probably both EF and NHibarnate will have the interfaces necessary to support this usage.
Not familiar with nhibernate, But usually, I go with your last solution(IList<T> GetAllWhereMeetsMyCriteria();
):
public IList<TEntity> Find<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
{
return this.Query<TEntity>().Where<TEntity>(criteria).ToList<TEntity>();
}
There's a great Genric Repository written at: http://www.martinwilley.com/net/code/nhibernate/genericrepository.html
You might want to use that since it covers more situations. Also, you can derive your custom repository from it for certain needs. (Example: http://www.martinwilley.com/net/code/nhibernate/productrepository.html)
UPDATE:
You can define custom criterias in your derived repositories and use them. e.g.
private ICriteria CriteriaCategoryId(int categoryId)
{
ICriteria criteria = Session.CreateCriteria(typeof(Product));
criteria.CreateCriteria("Category")
.Add(Expression.Eq("Id", categoryId));
return criteria;
}
public IList<Product> ProductByCategory(int categoryId, int pageStartRow, int pageSize)
{
var criteria = CriteriaCategoryId(categoryId)
.SetFirstResult(pageStartRow)
.SetMaxResults(pageSize);
return criteria.List<Product>();
}
精彩评论