0

I have an ASP.NET application with 3 layers UI, Service Layer and Repository.

I have to implement a search functionality on the products in the database. ProductRepository is my repository class and the signature of the method that gets all the products from the database is:

IQueryable<Product> GetAllProducts();

Therefore from my Service Layer I use:

IProductRepository _ProductRepository = new ProductRepository(); 

IQueryable<Product> products = _ProductRepository.GetAllProducts();

If I would like to filter IQueryable<Product> products, for instance by taking those product with price > 100 or just those with color = "yellow".

So I was wondering, instead of creating methods in ProductRepository such as:

IQueryable<Product> GetAllProductsByColor(int colorId)

I was wondering if it is a good practice to create in my Service Layer a set of methods that accept IQueryable<Product> as parameter and perform the filtering directly there:

IQueryable<Product> FilterProducts(IQueryable<Product> products, Dictionary<string, object> filters)

Where Dictionary<string, string> represent a set with (propertyName, value).

The advantage of this solution is that if I have to apply multiple filters I just pass the IQueryable<Product> already filtered, instead of taking everytime the intersection between the filtered product sets.

Is it a good practice (as long as I keep the context open) or it is not "allowed"by the multi-layered architecure pattern?

skaffman
  • 398,947
  • 96
  • 818
  • 769
CiccioMiami
  • 8,028
  • 32
  • 90
  • 151

1 Answers1

2

I'd say it depends on what provides the implementation of your IQueryable. In most cases, you run the risk of allowing users of your IQueryable to either 1) filter on fields that are not indexed (thus hosing the database layer, if you have any, when you have large tables), or 2) execute arbitrary IQueryable operations that put a huge load on the database (such as GroupBy's, Join's, etc.).

Assuming you are OK with 1, in order to prevent 2) I usually allow users to load objects through a IEnumerable<Product> LoadProducts(Predicate<Product> filter) method which I then transform into a Where on the IQueryable; this hides the other IQueryable methods and yet allows full flexibility for filtering purposes only.

Gabriele Giuseppini
  • 1,541
  • 11
  • 19
  • Thanks for your answer! I always return IQueryable from the Repository in order to allow deferred execution. My Service Layer returns always IEnumerable. May you explain with some example your techinique with Predicate? – CiccioMiami May 04 '12 at 15:22
  • 1
    Assume you have the following method: `IEnumerable LoadProducts(Expression> filter)`, then you can implement it as `return myQueryable.Where(filter)` and you can call it with a lambda-expression: `IEnumerable results = LoadProducts(p => p.Color == Colors.Red)` – Gabriele Giuseppini May 04 '12 at 15:44