1

我有这个代码:

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 端完成,而不是在返回的列表上

谢谢,

4

5 回答 5

3

您可以在允许 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>();
于 2012-09-19T18:16:50.613 回答
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>();
于 2012-09-19T08:42:34.453 回答
1
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; }
}

LINQPad 中的执行结果

于 2012-09-19T20:53:55.383 回答
0

通常你会这样做:

.OrderBy(x => x.yourField)

或者

.OrderByDescending(x => x.yourField)

如果您需要您的领域是动态的,请检查答案

于 2012-09-19T08:43:27.263 回答
0

如果该字段作为 a 传递string(例如,当使用 a 时ObjectDataSource),您可以使用 a 对其进行映射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是相当多的样板,但它确实非常可靠和快速。

于 2012-09-19T08:44:57.993 回答