【问题标题】:Create Order by programming Linq/Lambda通过编程 Linq/Lambda 创建订单
【发布时间】:2012-09-19 08:37:52
【问题描述】:

我有这个代码:

public void CreateOrdering(string field, string direction)
{
    //direction : ASC/DESC
    var result = context.MyTable
        .Where(x => x.Code > 5)
        .OrderBy()
        .Skip(10)
        .Take(5)
        .ToList<MyTable>();
}

我换个说法,我有一个方法,这个方法接收作为字符串字段名称用于排序和方向(“ASC”,“DESC”)

我想创建一个带有字段和在参数中收到的方向的订单。我必须能够:

  1. 我希望在这个查询中能够进行升序和降序
  2. 通过编程设置排序字段,这里Code可能是以后Id或者其他...
  3. 必须在 SQL Server 端进行排序,而不是在返回的列表中

谢谢,

【问题讨论】:

标签: c# .net linq c#-4.0 lambda


【解决方案1】:

您可以在允许 linq 语法的扩展方法中使用反射:

public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string direction)
{
    string orderByMethod = (direction == "ASC") ? "OrderBy" : (direction == "DESC" ? "OrderByDescending" : null);
    if(orderByMethod == null) throw new ArgumentException();

    var propertyInfo = typeof (TSource).GetProperty(field);
    var entityParam = Expression.Parameter(typeof(TSource), "e");
    Expression columnExpr = Expression.Property(entityParam, propertyInfo);
    LambdaExpression columnLambda = Expression.Lambda(columnExpr, entityParam);

    MethodInfo orderByGeneric =  typeof (Queryable).GetMethods().Single(m => m.Name == orderByMethod
                                                    && m.GetParameters().Count() == 2
                                                    && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
                                                    && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>));

    MethodInfo orderBy = orderByGeneric.MakeGenericMethod(new [] {typeof(TSource), propertyInfo.PropertyType});

    return (IQueryable<TSource>) orderBy.Invoke(null, new object[] { source, columnLambda });
}

使用示例:

internal class SomeType
{
    public string StringValue { get; set; }
}

IQueryable<SomeType> l = new List<SomeType>
    {
        new SomeType {StringValue = "bbbbb"},
        new SomeType {StringValue = "cccc"},
        new SomeType {StringValue = "aaaa"},
        new SomeType {StringValue = "eeee"},
    }.AsQueryable();

var asc = l.OrderBy("StringValue", "ASC");
var desc = l.OrderBy("StringValue", "DESC");

或者你的例子:

context.MyTable
        .Where(x => x.Code > 5)
        .OrderBy(field, direction)
        .Skip(10)
        .Take(5)
        .ToList<MyTable>();

【讨论】:

    【解决方案2】:

    我可能误解了你的问题,但你不能这样做:

    升序

    .OrderBy(x => x.Property)
    

    降序

    .OrderByDescending(x => x.Property)
    

    更新

    您需要的是Dynamic LINQ。但是,您尝试执行的操作可能会变得非常复杂。作为一个简单的解决方法,您可以执行以下操作:

    var result = context.MyTable
                        .Where(x => x.Code > 15);
    
    if (direction == "ASC")
    {
        result = result.OrderBy(field);
    }
    else
    {
        result = result.OrderByDescending(field);
    }
    
    result = result.Skip(10)
                   .Take(5)
                   .ToList<MyTable>();
    

    【讨论】:

    • 他希望 Property 成为一个动态字段
    【解决方案3】:
    void Main() {
        // Ascending by some other property
        CreateOrdering(item => item.SomeProperty, SortDirection.Ascending).Dump("Ascending order for SomeClass.SomeProperty");
        // Descending by some other property
        CreateOrdering(item => item.SomeProperty, SortDirection.Descending).Dump("Descending order for SomeClass.SomeProperty");
        // Ascending by the Code property
        CreateOrdering(item => item.Code, SortDirection.Ascending).Dump("Ascending order for SomeClass.Code");
        // Descending by the Code property
        CreateOrdering(item => item.Code, SortDirection.Descending).Dump("Descending order for SomeClass.Code");
    }
    
    // I reccomend not using bare strings, and instead use an enum
    public enum SortDirection {
         Ascending = 0,
         Descending = 1
    }
    // Define other methods and classes here
    public List<SomeClass> CreateOrdering<T>(Expression<Func<SomeClass, T>> field, SortDirection direction) {
        // query does not get executed yet, because we have not enumerated it.
        var query = context.MyTable
            .Where(x => x.Code > 5);
    
        if (direction.Equals(SortDirection.Ascending)) {
            query = query.OrderBy (field);
        } else {
            query = query.OrderByDescending (field);
        }
    
        // query gets executed when the call ToList is made.
        return query.Skip(10)
                    .Take(5)
                    .ToList();
    }
    
    public static class context {
        private static List<SomeClass> _MyTable = new List<SomeClass>() {
            new SomeClass("A", 4), new SomeClass("B", 5), new SomeClass("C", 6),
            new SomeClass("D", 7), new SomeClass("E", 8), new SomeClass("F", 9),
            new SomeClass("G", 10), new SomeClass("H", 11), new SomeClass("I", 12),
            new SomeClass("J", 13), new SomeClass("K", 14), new SomeClass("L", 15),
            new SomeClass("M", 16), new SomeClass("N", 17), new SomeClass("O", 18)
        };
    
        public static IQueryable<SomeClass> MyTable {
            get {
                return _MyTable.AsQueryable();
            }
        }
    }
    
    public class SomeClass {
        public SomeClass(string property, int code) {
            this.SomeProperty = property;
            this.Code = code;
        }
    
        public string SomeProperty { get; set; }
    
        public int Code { get; set; }
    }
    

    【讨论】:

    • 就像我的回答一样,只是更好地实现并且有很好的例子。删除了我的信息并为你 +1。
    【解决方案4】:

    通常你会这样做:

    .OrderBy(x => x.yourField)
    

    或

    .OrderByDescending(x => x.yourField)
    

    如果您需要动态字段,请查看this 答案

    【讨论】:

      【解决方案5】:

      如果该字段作为string 传递(例如使用ObjectDataSource 时),您可以使用switch 对其进行映射:

      var qry = context
        .MyTable
        .Where(x => x.Code > 5);
      
      switch(orderBy) {
        case "MyField": qry = qry.OrderBy(r => r.MyField); break;
        case "MyField DESC": qry = qry.OrderByDescending(r => r.MyField); break;
      }
      
      // By the way, ToList can infer the generic type if you don't
      // want to state it explicity
      var result = qry.Skip(10).Take(5).ToList();
      

      查询不会在ToList 之前执行,至少使用EF 是在SQL Server 上执行的。我承认switch 是相当多的样板,但它确实非常可靠和快速。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-06
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        • 2018-08-06
        • 2016-08-11
        相关资源
        最近更新 更多