Câu hỏi Làm cách nào để áp dụng OrderBy trên một IQueryable bằng cách sử dụng tên cột chuỗi trong một phương thức mở rộng chung?


public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
  where T : EntityObject
{
  var param = Expression.Parameter(typeof(T), "o");
  var body = Expression.PropertyOrField(param,columnName);

  var sortExpression = Expression.Lambda(body, param);
  return query.OrderBy(sortExpression);
}

Bởi vì loại cho OrderBy không được suy ra từ sortExpression tôi cần phải xác định nó một cái gì đó như thế này tại thời gian chạy:

var sortExpression = Expression.Lambda<T, TSortColumn>(body, param);

Hoặc là

return query.OrderBy<T, TSortColumn>(sortExpression);

Tôi không nghĩ rằng điều này là có thể tuy nhiên như TSortColumn chỉ có thể được xác định trong thời gian chạy.

Có cách nào để giái quyết vấn đề này không?


76
2017-11-21 01:15


gốc


Không chắc chắn nếu điều này những gì bạn đang tìm kiếm, nhưng hãy xem. Chúc mừng - joaopintocruz
@ JTew Làm thế nào tôi có thể thực hiện một đơn đặt hàng thứ hai theo mệnh đề. Id orderby sau đó theo ngày - SRJ


Các câu trả lời:


Chúng tôi đã làm một cái gì đó tương tự (không 100% giống nhau, nhưng tương tự) trong một dự án LINQ to SQL. Đây là mã:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
    return source.Provider.CreateQuery<T>(resultExp);
}

Chúng tôi đã không thực sự sử dụng một chung chung, chúng tôi đã có một lớp học được biết đến, nhưng nó sẽ làm việc trên một chung chung (tôi đã đặt giữ chỗ chung nơi nó nên được).

Chỉnh sửa: Đối với thứ tự giảm dần, hãy vượt qua OrderByDescending thay vì "OrderBy":

MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));

102
2017-11-21 02:01



Heh không có prob, tôi không thể gán câu trả lời cho bản thân mình anyways :) - JTew
cho thứ tự giảm dần, vượt qua trong "OrderByDescending" thay vì "OrderBy" MethodCallExpression resultExp = Expression.Call (typeof (Queryable), "OrderByDescending", ... - Garry English
Điều này làm việc tốt, nhưng sau đây chỉ là một ví dụ mã thực sự tốt đẹp: stackoverflow.com/questions/41244/dynamic-linq-orderby - BenSwayne
@Aaron Powell Làm thế nào tôi có thể thực hiện một đơn đặt hàng thứ hai theo mệnh đề ... id đặt hàng sau đó theo ngày - SRJ
Tham số là gì values cho? - Frank Fajardo


Bạn cũng có thể sử dụng Dynamic Linq

Thông tin ở đây http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Tải xuống C # tại đây http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Sau đó, chỉ cần thêm Linq.Dynamic sử dụng; và bạn sẽ tự động nhận được 2 phương pháp mở rộng bổ sung có thể được sử dụng như thế này

return query.OrderBy("StringColumnName");

27
2018-05-12 16:17



Cảm ơn, tôi đã nhìn thấy Linq.Dynamic về trong một mẫu tại trang web của Phil Haack nhưng không chắc chắn về nó. Tôi sẽ có một vở kịch với điều này vào cuối tuần. - JTew
Như một thay thế các Systems.Linq.Dynamic.dll có thể được tải về từ đây: github.com/kahanu/System.Linq.Dynamic - Baig


Tôi đã mở rộng các chức năng của bạn để thêm hỗ trợ cho Thuộc tính con.

private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class
{
    // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField).
    var parameter = Expression.Parameter(typeof(TEntity), "Entity");
    //  create the selector part, but support child properties
    PropertyInfo property;
    Expression propertyAccess;
    if (propertyName.Contains('.'))
    {
            // support to be sorted on child fields.
            String[] childProperties = propertyName.Split('.');
            property = typeof(TEntity).GetProperty(childProperties[0]);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);
            for (int i = 1; i < childProperties.Length; i++)
            {
                    property = property.PropertyType.GetProperty(childProperties[i]);
                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
            }
    }
    else
    {
            property = typeof(TEntity).GetProperty(propertyName);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);
    }
    resultType = property.PropertyType;                     
    // Create the order by expression.
    return Expression.Lambda(propertyAccess, parameter);
}

private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class
{
    Type type = typeof(TEntity);
    Type selectorResultType;
    LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType);
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
                                    new Type[] { type, selectorResultType },
                                    source.Expression, Expression.Quote(selector));
    return resultExp;
}

Bạn có thể sử dụng các chức năng này như:

GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName);

10
2017-11-21 14:00



Bạn là người hùng của tôi !! - Sebastián Guerrero
phải yêu người thông minh - Rod Johnson


Tôi đã sử dụng ý tưởng của bạn cho phương pháp mở rộng cho OrderBy. Nhưng trong trường hợp "nhiều đến nhiều" tôi nhận được lỗi. Ví dụ bạn có bảng Site, Customer và Customer_site. Đối với trang web nhất định, tôi muốn sắp xếp theo tên khách hàng và trong phần mở rộng OrderBy (khi tôi chuyển "site.customer" nơi khách hàng là tài sản điều hướng), tôi gặp lỗi trong dòng: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);

Đây là những gì tôi sử dụng (với một số cải tiến :-)):

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class
{
  IQueryable<TEntity> returnValue = null;

  string orderPair = orderByValues.Trim().Split(',')[0];
  string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy";

  var type = typeof(TEntity);
  var parameter = Expression.Parameter(type, "p");

  string propertyName = (orderPair.Split(' ')[0]).Trim();

  System.Reflection.PropertyInfo property;
  MemberExpression propertyAccess;

  if (propertyName.Contains('.'))
  {
    // support to be sorted on child fields. 
    String[] childProperties = propertyName.Split('.');
    property = typeof(TEntity).GetProperty(childProperties[0]);
    propertyAccess = Expression.MakeMemberAccess(parameter, property);

    for (int i = 1; i < childProperties.Length; i++)
    {
      Type t = property.PropertyType;
      if (!t.IsGenericType)
      {
        property = t.GetProperty(childProperties[i]);
      }
      else
      {
        property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
      }

      propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
    }
  }
  else
  {
    property = type.GetProperty(propertyName);
    propertyAccess = Expression.MakeMemberAccess(parameter, property);
  }

  var orderByExpression = Expression.Lambda(propertyAccess, parameter);

  var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },

  source.Expression, Expression.Quote(orderByExpression));

  returnValue = source.Provider.CreateQuery<TEntity>(resultExpression);

  if (orderByValues.Trim().Split(',').Count() > 1)
  {
    // remove first item
    string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1);
    return source.OrderBy(newSearchForWords);
  }

  return returnValue;
}

Trân trọng

Slobodan


8
2017-11-03 20:56





Có vẻ như điều này là cách để làm điều đó, bây giờ để xác minh rằng:

// ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
    typeof(Queryable),
    "OrderBy",
    new Type[] { queryableData.ElementType, queryableData.ElementType },
    whereCallExpression,
    Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****

6
2017-11-21 02:00



damnit, 34 giây sau! : P - Aaron Powell


Nếu bạn có thể thêm gói "System.Linq.Dynamic", Quá dễ dàng mà không có bất kỳ biến chứng nào,

gói insisll fisrt "System.Linq.Dynamic" từ trình quản lý gói NuGet rồi cố gắng như dưới đây là nhu cầu của bạn,

Ví dụ:

public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate,
                    List<string> sortBy, int pageNo, int pageSize = 12, params string[] include)
        {
            try
            {
                var numberOfRecordsToSkip = pageNo * pageSize;
                var dynamic = DbSet.AsQueryable();

                foreach (var s in include)
                {
                    dynamic.Include(s);
                }
                 return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize);


            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

Hy vọng điều này sẽ giúp


2
2018-02-12 17:42





Tôi đã sửa mã này một chút: https://stackoverflow.com/a/1670085/5852630

Mã này hoạt động với sắp xếp tuần tự: đầu tiên thực hiện "OrderBy", sau đó "ThenBy" (Không phải "OrderBy"!)

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class
{
    IQueryable<TEntity> returnValue = null;

    string[] orderPairs = orderByValues.Trim().Split(',');

    Expression resultExpression = source.Expression;

    string strAsc = "OrderBy";
    string strDesc = "OrderByDescending";

    foreach (string orderPair in orderPairs)
    {
        if (string.IsNullOrWhiteSpace(orderPair))
            continue;

        string[] orderPairArr = orderPair.Trim().Split(' ');

        string propertyName = orderPairArr[0].Trim();
        string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty;

        string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc;

        Type type = typeof(TEntity);
        ParameterExpression parameter = Expression.Parameter(type, "p");

        System.Reflection.PropertyInfo property;
        Expression propertyAccess;

        if (propertyName.Contains('.'))
        {
            // support to be sorted on child fields. 
            String[] childProperties = propertyName.Split('.');
            property = typeof(TEntity).GetProperty(childProperties[0]);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);

            for (int i = 1; i < childProperties.Length; i++)
            {
                Type t = property.PropertyType;
                if (!t.IsGenericType)
                {
                    property = t.GetProperty(childProperties[i]);
                }
                else
                {
                    property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
                }

                propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
            }
        }
        else
        {
            property = type.GetProperty(propertyName);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);
        }

        if (property.PropertyType == typeof(object))
        {
            propertyAccess = Expression.Call(propertyAccess, "ToString", null);
        }

        LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter);

        resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType },
            resultExpression, Expression.Quote(orderByExpression));

        strAsc = "ThenBy";
        strDesc = "ThenByDescending";
    }

    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression);

    return returnValue;
}

1
2018-05-04 13:01