1

I am trying to learn the repository pattern and looking at a generic repository I cannot see how to handle customized select statements. For example, using this article the author uses a select by ID and a select all.

public interface IGenericRepository<T> where T:class
{    
    IEnumerable<T> SelectAll();
    T SelectByID(object id);
    void Insert(T obj);    
    void Update(T obj);    
    void Delete(object id);    
    void Save();
}

Later the article the IGenericRepository interface is implemented using Northwind. Then that is used to create a Customer controller.

public class CustomerController : Controller
{    
    private IGenericRepository<Customer> repository = null; 
    public CustomerController()    
    {        
        this.repository = new GenericRepository<Customer>();    
    }
    ...

This would handle selecting a list of all Customers or for one Customer by ID but where I get stuck is some more real world examples like "select all Customers for a client" or "select all Customers for a region". Plus, you could have another controller based on a different entity that would filter on different attributes. I assume I'm missing something basic. If the user interface needed to present the Customer entity (or any other entity) by various filters, how would this be done by sticking with one generic repository?

pretzelb
  • 1,131
  • 2
  • 16
  • 38
  • FYI, there are some limitations when trying to create a Base class repository: http://stackoverflow.com/questions/15452377/c-sharp-object-composition-removing-boilerplate-code . Things start to get quite nasty if you have an Entity (like `Customer`) which should never really be deleted. – Philip Pittle Jul 25 '14 at 13:39
  • Thanks for that information. My next question was going on that very topic and customer is a good example. I have access to entities that I cannot control so update/delete are not possible. I wasn't sure about throwing exceptions for those cases. – pretzelb Jul 28 '14 at 22:25

3 Answers3

6

Here you go; to handle any select criteria apart from the Id, you can add Where method like below

public interface IGenericRepository<T> where T:class
{    
    IEnumerable<T> SelectAll();
    T SelectByID(object id);
    IEnumerable<T> Where(Expression<Func<T,bool>> predicate)// this one 
    void Insert(T obj);    
    void Update(T obj);    
    void Delete(object id);    
    void Save();
}

Now in the Where method implementation do it like this

public IEnumerable<T> Where(Expression<Func<T,bool>> predicate)
    {
        return _objectSet.Where(predicate).AsEnumerable();
    }

Here _objectSet in created in repository constructor like this :

public Repository(ObjectContext context)
        {
            _context = context;
            _objectSet = _context.CreateObjectSet<T>();
         }

public CustomerController()
    {
        _context = new NorthwindEntities();
        _reporsitory = new Repository<Customer>(_context);

    } 

Use of Where method like

 reporsitory.Where(c=>c.Country=="Canada").ToList();

For full reference see this project on codeplex (download /browse source code) https://efgenericrepository.codeplex.com/SourceControl/latest

Ajay Kelkar
  • 4,591
  • 4
  • 30
  • 29
  • Sorry for the delay, let me process your response a bit more. – pretzelb Jul 28 '14 at 22:18
  • I also found an interesting example using Contoso University on CodePlex here: https://contosontiermvc.codeplex.com/SourceControl/latest#ContosoUniveristy/Contoso.Data/Infrastructure/IRepository.cs – pretzelb Aug 01 '14 at 21:01
0

I think the implementation of the GenericRepository should somehow be able to return the IQueryable of current entity, like adding Get() method.

protected IQueryable<T> Get() // Notice that the access modifier is protected.
{
   return table;
}

Then you could just create a derived class from GenericRepository and add a select method that accepts the Filter class.

public class CustomerRepository : GenericRepository<Customer>
{
   public IEnumerable<T> SelectAll(CustomerFilter filter){ .. }
}

The filter class contains 2 filters.

public class CustomerFilter
{
   public int? ClientId { get; set; }
   public int? RegionId { get; set; }
}

Then the SelectAll implementation would be.

public IEnumerable<T> SelectAll(CustomerFilter filter)
{
   var query = Get();
   if (filter == null)
   {
       return query;
   }

   if (filter.ClientId.HasValue)
   {
      query = query.Where(q => q.ClientId == filter.ClientId.Value);
   }

   if (filter.RegionId.HasValue)
   {
      query = query.Where(q => q.RegionId == filter.RegionId.Value);
   }

   return query;
}

In the controller, calling it like.

public ActionResult Get(int? clientId, int? regionId)
{
    var filter = new CustomerFilter { ClientId = clientId, RegionId = regionId };
    var customers = _repository.SelectAll(filter); 
    return View();
}

You might need to see this post as your reference.

Community
  • 1
  • 1
Yuliam Chandra
  • 14,494
  • 12
  • 52
  • 67
0

An approach I've seen in one asp.net mvc based mission critical app, is to use the generic interface as defined in the question. Then there is an abstract class that implements that interface. And there is one more repository class that inherits the abstract class, which has all methods specific to that class.

public interface IGenericRepository<T> where T:class
{
 ...
}

public abstract class GenericRepository<T> : IGenericRepository where T:class
{
 ...
}

And the CustomerRepository class

public class CustomerRepository : GenericRepository<Customer>
{
 //add method specific to Customer like select Customers in specific country

}

And in the controller

public class CustomerController : Controller
{    
  private CustomerRepository  repository = null; 
  public CustomerController()    
  {        
    this.repository = new CustomerRepository();    
  }
  ...
gmail user
  • 2,753
  • 4
  • 33
  • 42