从对象和嵌套对象获取一些属性

时间:2015-04-09 09:45:10

标签: c# .net linq reflection lambda

我想返回一个只包含方法接收的字段和嵌套字段的对象(ExpandoObject)。

var fieldsToGet = new List<string> { "FirstName", "Id"};

当我这样做时:

.Select(x => Helpers.FilteringProperties(x, fieldsToGet))

我收到一个带有这两个值的对象,这是有效的。 我收到了FirstNameId

的对象

现在我想返回嵌套对象的一些属性:

var fieldsToGet = new List<string> { "FirstName", "Id", "Language.Name"};

我想收到这些属性: FirstNameIdLanguage.Name

下面的代码有效,但我希望保持足够的通用性并能够管理嵌套。

如何执行此通用操作,管理嵌套对象?

谢谢,

当前代码:

public static object FilteringProperties(object employee, List<string> fields)
{
    if (!fields.Any())
        return employee;
    else
    {
        ExpandoObject result = new ExpandoObject();
        foreach (var field in fields)
        {
            var fieldValue = employee.GetType()
            .GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
            .GetValue(employee, null);

            ((IDictionary<String, Object>)result).Add(field, fieldValue);
        }
        return result;
    }
}

示例类:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Language Language { get; set; }
    public int LanguageId { get; set; }
}

public class Language
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

4 个答案:

答案 0 :(得分:1)

我想你可以对“。” - 字符进行拆分,获取具有该名称的Property,然后通过递归调用自己的函数来获取它的值。

像这样(伪代码,可以更好)

if (field.Contains(".")) {
   var parts = field.Split('.');
   var fieldName = parts[0];
   List<string> toGet = new List<string>();
   toGet.Add(parts[1]); // this now contains everything after the "." 

   var fieldValue = employee.GetType()
        .GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
        .GetValue(employee, null);

   ((IDictionary<String, Object>)result).Add(field, FilteringProperties(fieldValue, toGet)
}

答案 1 :(得分:0)

使用递归方法。我相信它可以改进。

public static object FilteringProperties(object employee, List<string> fields)
{
    if (!fields.Any())
        return employee;
    else
    {
        ExpandoObject result = new ExpandoObject();
        foreach (var field in fields)
        {
            object fieldValue = null;

            Regex regex = new Regex("(\\w+)\\.(\\w+)");
            Match match = regex.Match(field);
            if (match.Success)
            {
                string className = match.Groups[1].Value;
                string propertyName = match.Groups[2].Value;

                var o = FilteringProperties(employee.GetType().GetProperty(className).GetValue(employee, null), new List<string>() {propertyName});
                var entry = (IDictionary<string, object>) o;
                fieldValue = entry[propertyName];
            }

            if(fieldValue == null)
                fieldValue = employee.GetType()
                .GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
                .GetValue(employee, null);

            ((IDictionary<String, Object>)result).Add(field, fieldValue);
        }
        return result;
    }
}

答案 2 :(得分:0)

这样的事似乎对我有用 -

public static ExpandoObject CreateObject(object parent, List<string> fields)
    {
        var expando = new ExpandoObject();
        var ret = (IDictionary<string,object>)expando;

        foreach (var property in fields)
        {
            //split to determine if we are a nested property.
            List<string> properties = property.Split('.').ToList();
            if (properties.Count() > 1)
            {
                //get our 'childs' children - ignoring the first item 
                var grandChildren = properties.Skip(1);

                //copy this child object and use it to pass back into this method recusivly - thus creating our nested structure
                var child = parent.GetType().GetProperty(properties[0]).GetValue(parent, null);
                //passing in the child object and then its children - which are grandchildren from our parent.
                ret.Add(properties[0], CreateObject(child, grandChildren.ToList()));
            }
            else //no nested properties just assign the property 
                ret.Add(property, parent.GetType().GetProperty(property).GetValue(parent));
        }

        return expando;
    }

答案 3 :(得分:0)

感谢Marc - guru来自这个网站 - 我提出了以下方法:

public static object GetFlattenPropertyValue(this object source, string flattenPropertyName) {

    var expression = source.GetType().CreatePropertyExpression(flattenPropertyName);

    if (expression != null) {
        var getter = expression.Compile();
        return getter.DynamicInvoke(source);
    }

    return null;
}

public static LambdaExpression CreatePropertyExpression(this Type type, string flattenPropertyName) {

    if (flattenPropertyName == null) {
        return null;
    }

    var param = Expression.Parameter(type, "x");
    Expression body = param;
    foreach (var member in flattenPropertyName.Split('.')) {
        body = Expression.PropertyOrField(body, member);
    }

    return Expression.Lambda(body, param);
}