【问题标题】:Using strings to access child properties in a list使用字符串访问列表中的子属性
【发布时间】:2016-01-06 14:39:04
【问题描述】:

我正在编写一个扩展方法,该方法允许我使用字符串而不是 Lambda 表达式对 IEnumerable 列表对象执行 OrderBy。它适用于简单的属性。但是,我正在尝试弄清楚如何允许嵌套属性。

如果我的模型看起来像这样:

public class Submission
{
    public int SubmissionId {get; set;}
    public string Description {get; set;}
    public int ProjectId {get; set;}
    // Parent object
    public Project ParentProject {get; set;}
}

public class Project
{
    public int ProjectId {get; set;}
    public string FullTitle {get; set;}
}

我可以用这个来下订单:

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);
            }

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

            if (descending)
                return list.OrderByDescending(x => prop.GetValue(x, null));
            else
                return list.OrderBy(x => prop.GetValue(x, null));
        }

        return list;
    }
}

我的代码会有这样的:

public List<Submission> SortedSubmissions (bool simple = true) {
    var project1 = new Project { ProjectId = 1, FullTitle = "Our Project"};
    var project2 = new Project { ProjectId = 2, FullTitle = "A Project"};

    List<Submission> listToSort = new List<Submission> 
    {
        new Submission { SubmissionId = 1, Description = "First Submission", 
                        ProjectId = project1.ProjectId, ParentProject = project1 } ,
        new Submission { SubmissionId = 2, Description = "Second Submission", 
                        ProjectId = project1.ProjectId, ParentProject = project1 } ,
        new Submission { SubmissionId = 3, Description = "New Submission", 
                        ProjectId = project2.ProjectId, ParentProject = project2 }
    };

    var simpleField = "Description";
    // This would have the submissions sorted (1, 3, 2)
    var simpleSort = listToSort.OrderByField(simpleField + " asc").ToList();


    // Need to see if I can get this to work
    var nestedField = "Project.FullTitle";
    // This would have the submissions sorted (3, 1, 2)
    return listToSort.OrderByField(nestedField + " asc").ToList();
}

我希望我能清楚地解释自己。这个可以吗?

更新:我使用了 André Kops 代码并在上面进行了调整,但出现此错误:System.Nullable'1[System.Int32]' cannot be used for return type 'System.Object'

【问题讨论】:

  • 如果你能举例说明你想发送什么样的字符串以及结果是什么,我认为这会很有帮助。
  • 最后一段代码显示了我将如何发送字符串“Project.FullTitle”,我希望列表的结果按父属性排序。
  • 如果我理解正确,您是在尝试按名称访问字符串中的字段吗?如果是这样,我认为在 C# 中没有像这样反映的方法。相反,我会设置一些条件(如果您知道这些字段最初的样子)。
  • 也许你应该看看 Dynamic Linq 以获得灵感weblogs.asp.net/scottgu/…
  • 您在输入列表中期望什么样的对象?它们是通用的,还是您自己创建的东西的列表?

标签: c#


【解决方案1】:

这是您代码中相当大的变化,但表达式树非常适合:

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;
    }
}

此示例还允许嵌套超过 2 个属性。

对于大型列表,它可能更快。

【讨论】:

  • 看起来不错,但是......在我的实际代码中,我有一些可以为空的整数字段。我明白了:'附加信息:'System.Nullable`1[System.Int32]' 类型的表达式不能用于返回类型'System.Object''
  • 找到了解决该问题的方法,并更新了您的帖子。非常感谢!
  • 奇怪的是,该错误只发生在可为空的整数上。你怎么修好它的?我没有看到任何更新?
  • 尝试编辑您的回复以添加修复程序,但需要审核。我在上面的代码中添加了额外的行。
  • 是的,这是正确的,尽管您需要使用它下面一行中的“转换”,而不是“propertyGetter”。我已经更新了我的答案。
【解决方案2】:

怎么样?

    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");
            }

            string fieldName;

            PropertyInfo parentProp = null;
            PropertyInfo prop = null;

            if (fullProperty.Contains("."))
            {
                // A nested property
                var parentProperty = fullProperty.Remove(fullProperty.IndexOf("."));
                fieldName = fullProperty.Substring(fullProperty.IndexOf("."));

                parentProp = typeof(T).GetProperty(parentProperty);
                prop = parentProp.PropertyType.GetProperty(fieldName);
            }
            else
            {
                // A simple property
                prop = typeof(T).GetProperty(fullProperty);
            }

            if (prop == null)
            {
                throw new Exception("No property '" + fullProperty + "' in + " + typeof(T).Name + "'");
            }

            if (parentProp != null)
            {
                if (descending)
                    return list.OrderByDescending(x => prop.GetValue(parentProp.GetValue(x, null), null));
                else
                    return list.OrderBy(x => prop.GetValue(parentProp.GetValue(x, null), null));
            }
            else
            {
                if (descending)
                    return list.OrderByDescending(x => prop.GetValue(x, null));
                else
                    return list.OrderBy(x => prop.GetValue(x, null));
            }
        }

        return list;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-19
    • 2011-12-24
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    相关资源
    最近更新 更多