Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repository pattern - too many methods

I often see example repository patterns that look like this:

public interface IRepository<T>
{
    T GetById(int id);
    IEnumerable<T> GetAllByName(string name);
}

But how do you handle cases where you may need to do a complex search? I don't think it would be a good idea to add many methods to the interface that would make it end up looking like:

IEnumerable<T> GetAllByFirstName(string name);
IEnumerable<T> GetAllByLastName(string name);
IEnumerable<T> GetAllByFirstAndLastName(string name);
IEnumerable<T> GetAllByAddress(string name);
...
...
...
like image 212
Books Avatar asked Feb 14 '12 08:02

Books


3 Answers

Use the Predicate Builder to dynamically build the where condition

public interface IRepository<T>
{
    T GetById(int id);

    IEnumerable<T> GetAllBy(Expression<Func<T, bool>> predicate);
}

Then build the conditions

  var predicate = PredicateBuilder.True<Customer>();
  if (!string.IsNullOrEmpty(FirstName))
  {
       predicate = predicate.And(d => d.FirstName.Contains(FirstName));
  }

  if (!string.IsNullOrEmpty(LastName))
  {
       predicate = predicate.And(d => d.LastName.Contains(LastName));
  }

  var products = productRepo.GetAllBy(predicate);

Create a class to pass the search criteria

public class CustomerFilterCriteria
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Address { get; set; }

}

public interface IRepository<T>
{
    T GetById(int id);
    IEnumerable<T> GetAllBy(CustomerFilterCriteria criteria);
}
like image 64
Eranga Avatar answered Oct 24 '22 00:10

Eranga


Question : Why make the repository generic if you are going to be exposing type specific methods (or do all your entities have FirstName, LastName, Address etc?).

If your underlying data resources are LINQ expression tree enabled, a common signature would also be generic, e.g.

IEnumerable<TEntity> Query(Expression<Func<TEntity, Boolean>> filter)

And if you want your queries to be 'stackable', you could expose an IQueryable

IQueryable<TEntity> Query(Expression<Func<TEntity, Boolean>> filter)
like image 2
StuartLC Avatar answered Oct 24 '22 00:10

StuartLC


You could add a filtermodel and check which filters have been filled like so:

IEnumerable<T> GetAllByFiilter(FilterModel filter);


public class FilterModel {
   public string Lastname {get;set;}
   public string Firstname {get;set;}
}


public IEnumerable<T> GetAllByFilter(FilterModel filter) {
   if(!string.IsNullOrWhiteSpace(filter.Lastname) { 
     //add restriction
   }
   // .. etc ..
}
like image 1
Pieter Avatar answered Oct 24 '22 00:10

Pieter