views:

75

answers:

1

I have a list of products coming from a repository. Simple enough. Now I want to add filtering and sorting. Sorting could happen outside of the repository since there are no efficiency gains doing them inside the repository (guess). I couldn't imagine doing filtering outside of the repository since we only want to load records we care about. I would think you would want to create a filter delegate and pass it to the repository.

The below code is pseudo C# code. What would functioning code look like to sort / filter?

The process below is really centered around passing delegates to the repository to filter:

Product myProduct = Repo.GetProducts( filter );

(if this were MS MVC, this code would exist in the controller):

Setup a filter:

//TODO: Need filter class definition
var filter = new Filter(); // made up object class for now
filter.AddCondition( field, operator, value);  // do this for each filter condition
filter.AddCondition( Product.Name, contains, "Hammer"); // Product.Name ?? (Example)

Product myProducts = Repo.GetProducts( filter ); // the Product call **FILTER**

Setup a sort:

// TODO: Need sort class definition
var sort = new Sort(); // another made up object class for now
sort.AddOrder( field, priority, sequenceUp) // Sequence enum is ascending/descending
sort.AddOrder( Product.Name, 1, ascending) // (Example) **SORT**

myProducts.AddSort(sort);

Return a view model:

// Next part strips off unnecessary fields for view (Presentation model)
// So we are not sending a huge data model to the view (hopefully good)
// TODO: Replace string with Service? function to extract a miniProduct from Product

MiniProduct myMinis = MakeMiniProductsFrom( myProducts);  // Service?

// Determine response type (XML, JSON, HTML View) and return appropriate data
// use "x-requested-by" as per Rob Conery noted below
if (Request.IsAjaxRequest()) return Json(myMinis);
else return View(myMinis);

As you can see, this code needs some help. I am really looking for sort and filter class code that could make this work, or links to outside sources. I assume sorting and filtering are a standard practice within DDD and design patterns, thus the question. Assume Product is a plain old e-commerce product ;) Rob Conery's Ajax notes are here

Thanks.

+3  A: 

Ideally, you'll probably want to do the sorting and the filtering inside the repository. This is particularly important if you're loading large collections, since the DB will most likely be able to sort more efficiently than you can do, and return the sorted results directly.

The IQueryable<T> interface is designed to handle this cleanly - it's the basis for Entity Framework, LINQ to SQL, and other databases with a LINQ provider. The nice thing about trying to support (at least at some level) IQueryable<T> is this lets you use your repository in a very standard way. For example, your pseudocode, using IQueryable<T>, might look more like:

IList<Product> myProducts = Repo.Products.Where( p => p.Category == theCategoryToFind ).OrderBy( p => p.Name );

Or, optionally, just:

var products = from p in Repo.Products
               where p.Category == theCategory
               order by p.Name
               select p;

I recommend looking at how some of the LINQ-oriented data access technologies work - there are quite a few open source options, such as Subsonic (as well as many commercial options) that may provide clues as to how you can better design this for usability.

Reed Copsey
So you are saying that the ORM should provide sorting and filtering for your POCO classes, say for instance if you use iterator patterns, etc.?
Dr. Zim
Ideally, yes. You want the ORM and data layer to handle sorting and filtering, because the DB should be handling this. By using IQueryable<T>, the actual filter + sort gets mapped directly to the DB.
Reed Copsey
For example then, Linq to Sql supports .Where. So I would use dc.Products.where( x => x.Name.contains("Hammer")), and keep adding .wheres as long as I had additional filters. So I should construct a big foreach statement in my Repository to accept a list of conditions then apply them as .where( ) methods? As far as sorting, I would do the same, like .OrderBy( x => x.Name) ? Construct these delegates in my controller, then pass them as parameters in the repository method?
Dr. Zim
If you just expose the IQueryable, you can pass the entire thing through your repo to LINQ to SQL. No need to make the delegates yourself - let the caller put the "where" clauses in and the order by for you.
Reed Copsey
Since you cannot serialize relations well because of "circular references", you would normally strip them out using select new myClassType { field1 = n.field1, field2 = n.field2, ...}. Since the .Where and .OrderBy would only be effective on the original select, would using the select new myClassType cause it to load all records before the where and orderby are applied?
Dr. Zim
No. IQueryable uses expression trees to build the query without the actual objects. This makes it very effective. I'd take a look at subsonic and other open source ORMs, to see how they work.
Reed Copsey