LINQ与嵌套属性值的反射

时间:2015-12-06 17:43:37

标签: c# .net linq reflection propertyinfo

我希望根据嵌套属性创建一个Linq Where。

让我们说这是我的项目:

public class Car {
    public Engine Engine { get; set; }
}

public class Engine {
    public int HorsePower { get; set; }
}

var myCar = new Car() {
    Engine = new Engine() {
        HorsePower = 400
    }
};

我在某处找到了这个代码,它允许创建Expression,

private Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>( PropertyInfo property, TValue value ) {
    var param = Expression.Parameter( typeof( TItem ) );
    var memberExp = Expression.Property( param, property );

    BinaryExpression body;

    //If nullable, Expression.Equal won't work even if the value is not null. So we convert to non nullable (the compared expression)
    Type typeIfNullable = Nullable.GetUnderlyingType( memberExp.Type );
    if ( typeIfNullable != null ) {
        var convertedExp = Expression.Convert( memberExp, Expression.Constant( value ).Type );
        body = Expression.Equal( convertedExp, Expression.Constant( value ) );
    } else {
        body = Expression.Equal( memberExp, Expression.Constant( value ) );
    }

    return Expression.Lambda<Func<TItem, bool>>( body, param );
}

现在,我想要一个等效的,调用我的方法PropertyEquals

var engine = myCar.Select( c => c.Engine.HorsePower == 400 );

这样的东西
var property = GetPropertyForDotSequence( typeof( Car ), "Engine.HorsePower" );
.Select( PropertyEquals<TEntity, int>( property , 400 ) );

我发现了一种允许基于点式格式(GetPropertyForDotSequence)查找属性的方法,该格式正常工作,但返回HorsePower属性信息,而不是Engine.HorsePower,因此我收到一条错误消息,说Car没有调用int32属性马力。

private PropertyInfo GetPropertyForDotSequence ( Type baseType, string propertyName ) {
        var parts = propertyName.Split( '.' );

        return ( parts.Length > 1 )
            ? GetPropertyForDotSequence( baseType.GetProperty( parts[ 0 ] ).PropertyType, parts.Skip( 1 ).Aggregate( ( a, i ) => a + "." + i ) )
            : baseType.GetProperty( propertyName );
    }

2 个答案:

答案 0 :(得分:1)

为了实现您的目标,不是使用单独的帮助函数从属性路径中提取最后属性信息,然后将属性信息传递给您的函数,所有这些都应该在内部完成函数本身,即它应该接收包含类似

的属性路径的string
public static partial class Utils
{
    public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(string propertyPath, TValue value)
    {
        var source = Expression.Parameter(typeof(TItem), "source");
        var propertyNames = propertyPath.Split('.');
        var member = Expression.Property(source, propertyNames[0]);
        for (int i = 1; i < propertyNames.Length; i++)
            member = Expression.Property(member, propertyNames[i]);
        Expression left = member, right = Expression.Constant(value, typeof(TValue));
        if (left.Type != right.Type)
        {
            var nullableType = Nullable.GetUnderlyingType(left.Type);
            if (nullableType != null)
                right = Expression.Convert(right, left.Type);
            else
                left = Expression.Convert(left, right.Type);
        }
        var body = Expression.Equal(left, right);
        var expr = Expression.Lambda<Func<TItem, bool>>(body, source);
        return expr;
    }
}

我不太确定它是如何有用的,因为签名不允许推断泛型类型,所以它需要这样的东西

var predicate = Utils.PropertyEquals<Car, int>("Engine.HorsePower", 400);
bool result = predicate.Compile().Invoke(myCar);

如果与以下扩展方法结合使用,IMO将非常有用

public static partial class Utils
{
    public static IQueryable<T> WherePropertyEquals<T, TValue>(this IQueryable<T> source, string propertyPath, TValue value)
    {
        return source.Where(PropertyEquals<T, TValue>(propertyPath, value));
    }
    public static IEnumerable<T> WherePropertyEquals<T, TValue>(this IEnumerable<T> source, string propertyPath, TValue value)
    {
        return source.Where(PropertyEquals<T, TValue>(propertyPath, value).Compile());
    }
}

所以你可以写这样的东西

List<Car> cars = new List<Car> { myCar };
var cars400 = cars.WherePropertyEquals("Engine.HorsePower", 400).ToList();

答案 1 :(得分:0)

您可以使用此方法从<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Exercitiul 1</title> <link rel="stylesheet" type="text/css" href="css/normalize.css"> <script src="scripts/script.js" type="text/javascript"></script> </head> <body> <form name="formular"> <table> <tr> <td>Ziua</td> <td><input type="text" name="zi"></td> </tr> <tr> <td>Luna</td> <td><input type="text" name="luna"></td> </tr> <tr> <td></td> <td><input type="button" value="Determinare zodie" id="buton"></td> </tr> <tr> <td>Zodia</td> <td><input type="text" name="zodie"></td> </tr> </table> </form> </body> </html>获取property值,其中嵌套属性名称为object

string

但这不是有效的public static object GetNestedPropertyValue(object obj, string nestedDottedPropertyName) { foreach (String part in nestedDottedPropertyName.Split('.')) { if (obj == null) return null; PropertyInfo info = obj.GetType().GetProperty(part); if (info == null) return null; obj = info.GetValue(obj, null); } return obj; } 声明

Linq

如果您有像这样的汽车对象,那么您可以做的是

var engine = myCar.Select( c => c.Engine.HorsePower == 400 );

您可以将var myCar = new Car() { Engine = new Engine() { HorsePower = 400 } }; 的值设为

Engine.HorsePower

修改

对于var horsePower = (int)GetNestedPropertyValue(myCar, "Engine.HorsePower"); 示例,如果您有Linq这样的

List<Car>

您可以将var myCar2 = new Car() { Engine = new Engine() { HorsePower = 800 } }; var cars = new List<Car> { myCar, myCar2 }; //myCar defined above 用作

Linq