tags:

views:

594

answers:

3

I would like to be able to refactor out the OrderBy clause in a linq expression.

Here is an example of a refactor of the where clause

before:

results = ctx.ActiveUsers
    .Where(u => u.CompanyID != 1 &&
           (u.LastName.ToLower().Contains(searchString)
           || u.Email.ToLower().Contains(searchString)
           || u.Company.Name.ToLower().Contains(searchString)))
    .OrderBy(u =>  u.LastName ).ThenBy(u => u.FirstName)
    .Select(u => new Employee {
      ID = u.ID
      , FirstName = u.FirstName
      , LastName = u.LastName
      , Email = u.Email
      , CompanyName = u.Company.Name
      , CompanyID = u.CompanyID.ToString() });


after:

results = ctx.ActiveUsers
    .Where(Employee.GetExpression(searchString))
    .OrderBy(u =>  u.LastName ).ThenBy(u => u.FirstName)
    .Select(u => new Employee {
      ID = u.ID
      , FirstName = u.FirstName
      , LastName = u.LastName
      , Email = u.Email
      , CompanyName = u.Company.Name
      , CompanyID = u.CompanyID.ToString() });


private static Expression<Func<User, bool>> GetExpression(string searchString)
{ 
    Expression<Func<User, bool>> p = (u => u.CompanyID != 1 &&
                       (u.LastName.ToLower().Contains(searchString)
                       || u.Email.ToLower().Contains(searchString)
                       || u.Company.Name.ToLower().Contains(searchString)));
    return p;
}

I was wondering if the same type of thing would be possible except I would like to refactor the Orderby expression.

Thank you in advance

+1  A: 

Assuming you want to actually take a string such as "LastName", "FirstName" etc, I'd do something like:

var unordered = ctx.ActiveUsers
                   .Where(Employee.GetExpression(searchString))
                   .OrderBy(ordering)
                   .Select(u => new Employee {
                       ID = u.ID,
                       FirstName = u.FirstName,
                       LastName = u.LastName,
                       Email = u.Email,
                       CompanyName = u.Company.Name,
                       CompanyID = u.CompanyID.ToString() });

and add a new OrderBy extension method:

public static class UserQueryableExtensions
{
    public static IOrderedQueryable<User> OrderBy(this IQueryable<User> source,
                                                  string ordering)
    {
        switch (ordering)
        {
            case "LastName":
                return source.OrderBy(x => x.LastName);
            case "FirstName":
                return source.OrderBy(x => x.FirstName);
            case "Email":
                return source.OrderBy(x => x.Email);
            case "Company":
                return source.OrderBy(x => x.Company);
            default:
                throw new ArgumentException("Unknown ordering");
        }
    }
}

You certainly could do this using reflection, but unless you have a significant set of properties (or you want to use the same routine for different entity types) a switch statement is easier.

Jon Skeet
A: 

I'd agree with Jon on using lambdas where possible to avoid typos etc. However, if you genuinely can't do this (for whatever reason), I've looked at fully-dynamic OrderBy in the past. See here for an example tested on LINQ-to-SQL (but it should be OK with EF in theory).

Marc Gravell
A: 

thanks jon,

that answer put me on the right path for now... However, I need to preserve the ThenBy clause in my order. so while the solution below is not dynamic it still preserves the ThenBy

var unordered = ctx.ActiveUsers
                   .Where(Employee.GetExpression(searchString))
                   .MyOrder()
                   .Select(u => new Employee {
                       ID = u.ID,
                       FirstName = u.FirstName,
                       LastName = u.LastName,
                       Email = u.Email,
                       CompanyName = u.Company.Name,
                       CompanyID = u.CompanyID.ToString() });


public static class UserQueryableExtensions
{
    public static IOrderedQueryable<User> MyOrder(this IQueryable<User> source )
    { 
        return source.OrderBy(x => x.LastName).ThenBy(x => x.FirstName).ThenBy(x => x.Email);
    }
}
eiu165