Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq sort child in query

I have an EF model as follows:

enter image description here

On this model, I can obviously use Categories.Products to receive a list of products.

I have a query as follows to return a list of Categories, with Products as a List to be used in an ASP.NET MVC 3 view:

var categories = (from a in context.Categories.Include("Products")
                  orderby a.SortOrder ascending
                  select a).ToList();
return View(categories);

In order to show the Products in order of their SortOrder I currently have to do:

<ul>
@foreach (var category in Model)
{
    <li>@category.Title
        <ul>
        @foreach (var product in category.Products.OrderBy(a => a.SortOrder))
        {
            <li>@product.Description</li>
        }
        </ul>
    </li>
}
</ul>

The offending line is: @foreach (var product in category.Products.OrderBy(a => a.SortOrder)) as this is handling a bit of my model in the view.

Is there a way to sort this in the query?

like image 418
Darbio Avatar asked Jun 23 '11 02:06

Darbio


1 Answers

Is there a way to sort this in the query?

View models of course:

public class CategoryViewModel
{
    public string Title { get; set; }
    public IEnumerable<ProductViewModel> Products { get; set; }
}

public class ProductViewModel
{
    public string Description { get; set; }
}

and in your controller action do the necessary to fill this view model:

public ActionResult Index()
{
    var categories = 
        (from category in context.Categories
         orderby category.SortOrder ascending
         select new CategoryViewModel
         {
             Title = category.Title,
             Products = category
                 .Products
                 .OrderBy(p => p.SortOrder)
                 .Select(p => new ProductViewModel
                 {
                     Description = p.Description
                 })
             }).ToList(); 
        ).ToList();
    return View(categories);
}

and in the Index.cshtml view you can get rid of ugly loops and use display templates:

@model IEnumerable<CategoryViewModel>
<ul>
    @Html.DisplayForModel()
</ul>

and inside the display template for a category (~/Views/Shared/CategoryViewModel.cshtml)

@model CategoryViewModel
<li>
    @Html.DisplayFor(x => x.Title)
    <ul>
        @Html.DisplayFor(x => x.Products)
    </ul>
</li>

and inside the display template for a product (~/Views/Shared/ProductViewModel.cshtml)

@model ProductViewModel
<li>
    @Html.DisplayFor(x => x.Description)
</li>

As a further improvement to the controller action you could use AutoMapper to map between your domain models (EF objects) and your view models which should be passed to a view.

like image 138
Darin Dimitrov Avatar answered Oct 18 '22 21:10

Darin Dimitrov