【问题标题】:Dynamically build an Expression tree for sorting动态构建表达式树进行排序
【发布时间】:2016-05-09 20:35:28
【问题描述】:

我的项目中有一个扩展,可以让我用字符串对IEnumerable 进行排序,这样可以更动态地进行排序。

所以,如果我有这些模型:

public MyModel
{
    public int Id {get; set;}
    public string RecordName {get; set;}
    public ChildModel MyChild {get; set;}
}

public ChildModel 
{
    public int ChildModelId {get; set;}
    public DateTime SavedDate {get; set;}
}

我可以通过两种方式排序:

myList.OrderByField("RecordName ");

myList.OrderByField("MyChild.SavedDate");

但是,如果我的对象具有 ICollection 属性,例如 ICollection<ChildModel> MyChildren,我可以像这样对我的排序进行硬编码:

myList
    .OrderBy(m => m.MyChildren
        .OrderByDescending(c => c.SavedDate).FirstOrDefault().SavedDate);

得到我想要的。

我的问题是,我怎样才能更新我的扩展方法以允许获得相同的结果:

myList.OrderByField("MyChildren.SavedDate");

这是我当前的扩展:

public static class MkpExtensions
{
    public static IEnumerable<T> OrderByField<T>(this IEnumerable<T> list, string sortExpression)
    {
        sortExpression += "";
        string[] parts = sortExpression.Split(' ');
        bool descending = false;
        string fullProperty = "";

        if (parts.Length > 0 && parts[0] != "")
        {
            fullProperty = parts[0];

            if (parts.Length > 1)
            {
                descending = parts[1].ToLower().Contains("esc");
            }

            ParameterExpression inputParameter = Expression.Parameter(typeof(T), "p");
            Expression propertyGetter = inputParameter;
            foreach (string propertyPart in fullProperty.Split('.'))
            {
                PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
                if (prop == null)
                    throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
                propertyGetter = Expression.Property(propertyGetter, prop);
            }

            Expression conversion = Expression.Convert(propertyGetter, typeof(object));
            var getter = Expression.Lambda<Func<T, object>>(conversion, inputParameter).Compile();

            if (descending)
                return list.OrderByDescending(getter);
            else
                return list.OrderBy(getter);
        }

        return list;
    }
}

我正在考虑检查prop 的类型并执行if... else 语句,但我不确定。

可能是这样的:

foreach (string propertyPart in fullProperty.Split('.'))
{
    var checkIfCollection = propertyGetter.Type.GetInterfaces()//(typeof (ICollection<>).FullName);
        .Any(x => x.IsGenericType &&
            (x.GetGenericTypeDefinition() == typeof(ICollection<>) || x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));

    if (checkIfCollection)
    {
        // Can I get this to do something like 
        // myList.OrderBy(m => m.MyChildren.Max(c => c.SavedDate));

        // So far, I can get the propertyGetter type, and the type of the elements:
        var pgType = propertyGetter.Type;
        var childType = pgType.GetGenericArguments().Single();

        // Now I want to build the expression tree to get the max
        Expression left = 
            Expression.Call(propertyGetter, pgType.GetMethod("Max", System.Type.EmptyTypes));
        // But pgType.GetMethod isn't working
    }
    else
    {
        PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
        if (prop == null)
            throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
        propertyGetter = Expression.Property(propertyGetter, prop);
    }
}

【问题讨论】:

  • 仅供参考,您可以像这样简化硬编码的嵌套排序:myList.OrderBy(m =&gt; m.MyChildren.Max(c =&gt; c.SavedDate));。说到这里,当你的程序看到"MyChildren.SavedDate"时,它怎么知道按MyChildren升序排序,但按SavedDate降序排序?
  • Max 将适用于子记录。在我的字符串中,我实际上使用MyChildren.SavedDate ascMyChildren.SavedDate desc 并相应地翻转。 .SavedDate 假设是最近的……暂时。 ;)

标签: c# linq sorting expression-trees


【解决方案1】:

Max 函数是扩展方法,不是IEnumerableICollection 的成员方法。您必须从它的类 Enumerable 中调用它。

这里是一个如何通过表达式树调用Max的例子:

IEnumerable<int> list = new List<int> { 3, 5, 7, 2, 12, 1 };
var type = typeof(Enumerable); //This is the static class that contains Max

//Find The overload of Max that matches the list
var maxMethod = type.GetMethod("Max", new Type[] { typeof(IEnumerable<int>) });
ParameterExpression p = Expression.Parameter(typeof(IEnumerable<int>));

//Max is static, so the calling object is null
var exp = Expression.Call(null, maxMethod, p); 
var lambda = Expression.Lambda<Func<IEnumerable<int>, int>>(exp, p);
Console.WriteLine(lambda.Compile()(list));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多