2017-08-28 8 views
1

J'ai une fonction IOrderedQueryable personnalisée comme ci-dessous.# Ordre par Not Null avec extension d'expression linq

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> srcQuery, 
string orderColumn, bool isAscending) 
{ 
    var type = typeof(T); 
    var property = type.GetProperty(orderColumn); 

    if (property == null) 
    throw new Exception("Column property \"" + orderColumn + "\" does not exist on the type \"" + typeof(T).FullName + "\""); 

    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var orderByExp = Expression.Lambda(propertyAccess, parameter); 
    MethodCallExpression resultExp = 
    Expression.Call(typeof(Queryable),isAscending ? "OrderBy" : 
"OrderByDescending", new Type[] { type, property.PropertyType }, 
    srcQuery.Expression, Expression.Quote(orderByExp)); 

    return (IOrderedQueryable<T>)srcQuery.Provider.CreateQuery<T>(resultExp); 
} 

Je voudrais savoir s'il y a de toute façon je pouvais faire une commande par la propriété condition! = Null pour afficher pas les valeurs NULL en premier, suivi par les valeurs NULL dans l'expression orderByExp ci-dessus

+0

est-ce pas possible d'utiliser dans votre cas? 'OrderBy (x => x.property == null? 0: 1)' – Pac0

Répondre

2

Vous peut modifier l'accès à la propriété pour effectuer l'équivalent de query.OrderBy(x => x.property == null ? 0 : 1)

public static IOrderedQueryable<T> OrderByNull<T>(IQueryable<T> srcQuery, string orderColumn, bool isAscending) 
{ 
    var type = typeof(T); 
    var property = type.GetProperty(orderColumn); 

    if (property == null) 
     throw new Exception("Column property \"" + orderColumn + "\" does not exist on the type \"" + typeof(T).FullName + "\""); 

    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.Condition(
     Expression.Equal(Expression.MakeMemberAccess(parameter, property), Expression.Constant(null, property.PropertyType)), 
     Expression.Constant(1), 
     Expression.Constant(0)); 

    var orderByExp = Expression.Lambda(propertyAccess, parameter); 
    MethodCallExpression resultExp =Expression.Call(typeof(Queryable), isAscending ? "OrderBy" : "OrderByDescending", new Type[] { type, typeof(int) }, 
    srcQuery.Expression, Expression.Quote(orderByExp)); 

    return (IOrderedQueryable<T>)srcQuery.Provider.CreateQuery<T>(resultExp); 
} 

note: Si la propriété est pas annulable (par exemple int? ou double?) une erreur se produit,

Modifier:

La version ci-dessus ce ne fonctionne que pour les propriétés nullables. Une autre version serait à l'ordre par une valeur par défaut pour les colonnes nullable et de faire la commande normale pour tous les autres alias: if (property is nullable) query.OrderBy(x => x.property == null ? default(basePropertyType) : x.property) else query.OrderBy(x => x.property)

La version pour cela ressemblerait à ceci:

static Dictionary<Type, object> DefaultTypeValues = new Dictionary<Type, object> 
{ 
    { typeof(string), "" }, 
    // { typeof(DateTime?), new DateTime(1753,1,1) } // Min date for sql date 
     { typeof(DateTime?), new DateTime(9999,12,31} // Max date for sql date 
}; 

public static object GetDefaultValue(Type t) 
{ 
    object defaultValue; 
    if(!DefaultTypeValues.TryGetValue(t, out defaultValue)) 
    { 
     if(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) 
     { 
      defaultValue = Activator.CreateInstance(t.GetGenericArguments().Single()); 
     } 
     else 
     { 
      throw new NotSupportedException("Could not get default value for type " + t.FullName + " consider adding it in DefaultTypeValues"); 
     } 
    } 
    return defaultValue; 
} 
public static IOrderedQueryable<T> OrderBy<T>(IQueryable<T> srcQuery, string orderColumn, bool isAscending) 
{ 
    var type = typeof(T); 
    var property = type.GetProperty(orderColumn); 

    if (property == null) 
     throw new Exception("Column property \"" + orderColumn + "\" does not exist on the type \"" + typeof(T).FullName + "\""); 

    var parameter = Expression.Parameter(type, "p"); 
    // default sort is performed by o=> o.Prop 
    Expression propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var propType = property.PropertyType; 

    // If property is nullable we add teh null check 
    if (propType == typeof(string) || (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>))) 
    { 
     var defaultValue = GetDefaultValue(propType); 
     // If the property is nullable we sort by (o => o.Prop == null ? default(propType) : o.Prop) 
     propertyAccess = Expression.Condition(
      Expression.Equal(propertyAccess, Expression.Constant(null, propType)), 
      Expression.Constant(defaultValue, propType), 
      propertyAccess 
     ); 
    } 

    var orderByExp = Expression.Lambda(propertyAccess, parameter); 
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), isAscending ? "OrderBy" : "OrderByDescending", new Type[] { type, propType }, 
    srcQuery.Expression, Expression.Quote(orderByExp)); 

    return (IOrderedQueryable<T>)srcQuery.Provider.CreateQuery<T>(resultExp); 
} 

La valeur par défaut est personnalisable par tapez dans le DefaultTypeValues ​​ou si aucune valeur n'est spécifiée, il sera par défaut au type nullable sous-jacent (ex pour int? ce sera 0)

+0

Salut Titian Merci pour votre réponse. Le champ que j'ai actuellement est la date Nullable. Est-ce que ce qui précède fonctionnera pour la même chose? – rockingmeister

+0

Devrait travailler pour n'importe quel 'Nullable', je viens de tester avec' DateTime? 'Et la requête a été générée –

+0

désolé, je n'ai pas posté la version finale de mon code. Fait un petit changement devrait fonctionner, mais je ne peux pas tester maintenant parce que je suis au lit. Si ça ne marche pas, faites le moi savoir et je trouverai une solution demain. –