views:

73

answers:

4

I have an IQueryable<Product> that needs to be sorted by Name. Each Product has an IQueryable<Category> that also needs to be sorted by Name. I'm having a hard time expressing this in Linq. I could loop through the products and sort each category list, but it seems messy. Hoping a Linq ninja has a smarter solution.

My Product class looks like:

public class Product {
    public string Name { get; set; }
    public IQueryable<Category> Categories { get; set; }
}

My Category class looks like:

public class Category { 
    public string Name { get; set; }
}

Currently, I'm doing this:

 var myProducts = products.OrderBy(x => x.Name).Select(x => new Product { 
    Name = x.Name,
    Categories = x.Categories(y => y.Name)
});

The trouble is, when using something like NHibernate, this creates new Product objects, essentially disconnecting the product from the NH session.

A: 

Maybe this is what you want? You do not get a list of products that each has a list of categories, however. That's impossible using LINQ because you can not alter a collection using LINQ, you can only make a new collection from an existing one (in my example p.Categories is the existing collection, SortedCategories the new one).

from p in products
orderby p.Name
select new
{
    Product = p,
    SortedCategories = from c in p.Categories
                       orderby c.Name
                       select c
}

You say you have IQueryable's. Does that mean that you are querying against a database using this LINQ statement? I'm not sure how well this transforms to SQL (performance-wise).

Ronald Wildenberg
A: 

Why not just sort the enumerable in place?

products = products.OrderBy(x => x.Name);
products.ToList().ForEach(x => x.Categories = x.Categories.OrderBy(y => y.Name));

As others have stated, this may perform poorly if you're connected.

arootbeer
A: 

You don't want to modify the Product object in any way that could affect persistence and you don't want to create new Product instances.

So add this to Product class:

public IOrderedEnumerable<Category> CategoriesOrderedByName
{
  get { return this.Categories.OrderBy(y => y.Name); }
}

And use product.CategoriesOrderedByName instead when you need the sorted version in your UI code.

Without this anyone using your class has no expectation that the Category objects are sorted in any way. With this you are being explicit in informing consumers of your class what to expect and that you intend to always return them in a sorted order. You can also use IOrderedEnumerable<> as the return type to make allow further sub-sorting using ThenBy().

Hightechrider
A: 

If you want to enumerate over the Product objects in sorted order and within each Product you want the Categories in sorted order, you could do something like this.

var sorted = products
    .Select(
        p => new
        {
            Product = p,
            Categories = p.Categories.OrderBy(c => c.Name)
        }
    )
    .OrderBy(x => x.Product.Name);

It looks like this is basically what Ronald has, only without using LINQ syntax.

Dan Tao