简体   繁体   English

如何在实体框架中动态构造 Order By Expression?

[英]How to construct Order By Expression dynamically in Entity Framework?

I used the following methods to construct Order By Expression .我使用以下方法来构造Order By Expression Original Source 原始来源

It is really slick.它真的很光滑。 The downside is it only works if Property is string type.缺点是它仅在属性为字符串类型时才有效。

How can I make it to accept different Property type without creating a bunch of methods for different data types ?如何在不为不同数据类型创建一堆方法的情况下使其接受不同的 Property 类型

public static bool PropertyExists<T>(string propertyName)
{
    return typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase |
      BindingFlags.Public | BindingFlags.Instance) != null;
}

public static Expression<Func<T, string>> GetPropertyExpression<T>(string propertyName)
{
    if (typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase | 
        BindingFlags.Public | BindingFlags.Instance) == null)
    {
        return null;
    }

    var paramterExpression = Expression.Parameter(typeof(T));

    return (Expression<Func<T, string>>)Expression.Lambda(
        Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
}

Usage用法

// orderBy can be either Name or City.
if (QueryHelper.PropertyExists<Club>(orderBy)) 
{ 
   var orderByExpression = QueryHelper.GetPropertyExpression<Club>(orderBy); 
   clubQuery = clubQuery.OrderBy(orderByExpression); 
} 
else 
{ 
   clubQuery = clubQuery.OrderBy(c => c.Id); 
} 

Problem问题

public class Club 
{ 
  public int Id { get; set; } 
  public string Name { get; set; } 
  public string City { get; set; } 
  public DateTime CreateDate { get; set; } <= this won't work
} 

My Current Approach (Too many if statements)我目前的方法(if 语句太多)

public static Expression<Func<TSource, TKey>> 
    GetPropertyExpression<TSource, TKey>(string propertyName)
{
    if (typeof (TSource).GetProperty(propertyName, BindingFlags.IgnoreCase | 
        BindingFlags.Public | BindingFlags.Instance) == null)
    {
        return null;
    }
    var paramterExpression = Expression.Parameter(typeof (TSource));
    return (Expression<Func<TSource, TKey>>) 
        Expression.Lambda(Expression.PropertyOrField(
           paramterExpression, propertyName), paramterExpression);
}

The downside is I end up with a lot of if statements for each datatype.缺点是每个数据类型都有很多 if 语句。

if (QueryHelper.PropertyExists<Club>(orderBy)) 
{
   if(orderBy == "CreateDate")
   {       
      var orderByExpression = GetPropertyExpression<Club, DateTime>(orderBy);
      ...
   }
   else if(orderBy == "Name" || orderBy == "City")
   {
      var orderByExpression = GetPropertyExpression<Club, string>(orderBy);
      ...
   }
   ...
}
else 
{ 
   clubQuery = clubQuery.OrderBy(c => c.Id); 
} 

I found a solution with the help of Jon Skeet 's old answer.我在Jon Skeet的旧答案的帮助下找到了解决方案。

public static class QueryHelper
{
    private static readonly MethodInfo OrderByMethod =
        typeof (Queryable).GetMethods().Single(method => 
        method.Name == "OrderBy" && method.GetParameters().Length == 2);

    private static readonly MethodInfo OrderByDescendingMethod =
        typeof (Queryable).GetMethods().Single(method => 
        method.Name == "OrderByDescending" && method.GetParameters().Length == 2);

    public static bool PropertyExists<T>(this IQueryable<T> source, string propertyName)
    {
        return typeof(T).GetProperty(propertyName, BindingFlags.IgnoreCase |
            BindingFlags.Public | BindingFlags.Instance) != null;
    }

    public static IQueryable<T> OrderByProperty<T>(
       this IQueryable<T> source, string propertyName)
    {
        if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase | 
            BindingFlags.Public | BindingFlags.Instance) == null)
        {
            return null;
        }
        ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
        Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
        LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
        MethodInfo genericMethod = 
          OrderByMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
        object ret = genericMethod.Invoke(null, new object[] {source, lambda});
        return (IQueryable<T>) ret;
    }

    public static IQueryable<T> OrderByPropertyDescending<T>(
        this IQueryable<T> source, string propertyName)
    {
        if (typeof (T).GetProperty(propertyName, BindingFlags.IgnoreCase | 
            BindingFlags.Public | BindingFlags.Instance) == null)
        {
            return null;
        }
        ParameterExpression paramterExpression = Expression.Parameter(typeof (T));
        Expression orderByProperty = Expression.Property(paramterExpression, propertyName);
        LambdaExpression lambda = Expression.Lambda(orderByProperty, paramterExpression);
        MethodInfo genericMethod = 
          OrderByDescendingMethod.MakeGenericMethod(typeof (T), orderByProperty.Type);
        object ret = genericMethod.Invoke(null, new object[] {source, lambda});
        return (IQueryable<T>) ret;
    }
}

Usage用法

string orderBy = "Name";
if (query.PropertyExists(orderBy))
{
   query = query.OrderByProperty(orderBy);
   - OR - 
   query = query.OrderByPropertyDescending(orderBy);
}

this can done using expression : check this sample这可以使用表达式完成:检查此示例

public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable, string propertyOrFieldName, bool ascending = true)
    {
        var elementType = typeof (T);
        var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";

        var parameterExpression = Expression.Parameter(elementType);
        var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, propertyOrFieldName);
        var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);

        var orderByExpression = Expression.Call(typeof (Queryable), orderByMethodName,
                                                new[] {elementType, propertyOrFieldExpression.Type}, queryable.Expression, selector);

        return queryable.Provider.CreateQuery<T>(orderByExpression);
    }

I have, IMHO, a simpler solution:恕我直言,我有一个更简单的解决方案:

public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, bool ascending , string sortingProperty)
{
    if (ascending)
        return source.OrderBy(item => item.GetReflectedPropertyValue(sortingProperty));
    else
        return source.OrderByDescending(item => item.GetReflectedPropertyValue(sortingProperty));
}

private static object GetReflectedPropertyValue(this object subject, string field)
{
    return subject.GetType().GetProperty(field).GetValue(subject, null);
}

The usage is:用法是:

myQueryableCollection.Sort(ascending: true, "Name")

Of course, the PropertyExist() helper is a great addition...当然, PropertyExist()助手是一个很好的补充......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM