1

Here is the problem:

We use table object to allow the users to perform some functionality like search, sort, pagination etc. Those tables works great. But there is a problem with one of the functionality : The sort (=OrderBy).

In fact, to allow the sort, we set in every column a string that represent the expression: For example, if the expression is Person => Person.Id, then the string is Id; if the expression is Person => Person.Address.Street, the string is Address.Street.

In the first case (Person => Person.Id), it works great since it is not a sub object. But in the second case (Person => Person.Address.Street), it doesn't since the Address object could be null.

To allow the Orderby to be performed from a string, I found on an other post the following methods :

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "OrderBy");
}

public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "OrderByDescending");
}

public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "ThenBy");
}

public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
    return ApplyOrder<T>(source, property, "ThenByDescending");
}

private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
    string[] props = property.Split('.');
    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach (string prop in props)
    {
        // use reflection (not ComponentModel) to mirror LINQ 
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
    object result = typeof(Queryable).GetMethods().Single(
    method => method.Name == methodName
        && method.IsGenericMethodDefinition
        && method.GetGenericArguments().Length == 2
        && method.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(T), type)
            .Invoke(null, new object[] { source, lambda });
    return (IOrderedQueryable<T>)result;
}

Does any of you have an idea that would allow me to add a condition that would not select the object with subobject == null ? Or prevent it from Trying to access a property from an object that is null ?

EDIT :

The check would look something like : list.OrderBy(x => (x.Address != null) ? x.Address.Street : string.Empty).

So I need to add a null check on every object between x and the final field. Is it possible to do that using those methods ?

EDIT 2 :

I've tried to replace

Expression.Property(expr, pi);

by

expr = Expression.Condition(
                Expression.Equal(expr, Expression.Constant(null)),
                Expression.Constant(String.Empty),
                Expression.Property(expr, pi));

But it seems it doesn't work. I get the following exception :

Argument types do not match

Any idea how I'm supposed to know the default value for the field accessed by expr ?

Whoami
  • 334
  • 4
  • 16
  • Can you check if the property is `null` before you call the OrderBy method? – Mentoliptus Feb 01 '12 at 09:01
  • That's what I don't want to do. I would rather include an other part to the expression that would check for me so I don't have to split the code in multiple parts. But I don't know how to include this check. – Whoami Feb 01 '12 at 09:26
  • See my answer here http://stackoverflow.com/questions/17647627/dynamic-linq-order-by-on-nested-property-with-null-properties/31450534#31450534 – Chuck Norris Jul 17 '15 at 09:57

1 Answers1

0

create your null constant as the property type.

expr = Expression.Condition(Expression.Equal(expr, Expression.Constant(null, expr.Type)),
                Expression.Constant(String.Empty),
                Expression.Property(expr, pi));
jkrieg
  • 116
  • 4