Dynamically creating an expression which selects an objects property

大兔子大兔子 提交于 2020-01-13 13:04:05

问题


I want to be able to build up an expression dynamically, which is essentially a property selector.

I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.

I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.

The library takes an argument of :

Expression<Func<TObject, TPropertyType>>

An example of how this would be coded if baked into an application would be :

Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;

However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.

So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.

psuedo code :

string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property

 Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];

Update :

I have got as far as this

ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);

But the problem I have with this is that the return type is not always an int but may be some other type.


回答1:


Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.

public static class QueryableExtension
{
    public static object Build<Tobject>(this Tobject source, string propertyName)
    {
        var propInfo = typeof(Tobject).GetProperty(propertyName);

        var parameter = Expression.Parameter(typeof(Tobject), "x");

        var property = Expression.Property(parameter, propInfo);

        var delegateType = typeof(Func<,>)
                           .MakeGenericType(typeof(Tobject), propInfo.PropertyType);

        var lambda = GetExpressionLambdaMethod()
                        .MakeGenericMethod(delegateType)
                        .Invoke(null, new object[] { property, new[] { parameter } });

        return lambda;
    }

    public static MethodInfo GetExpressionLambdaMethod()
    {
       return typeof(Expression)
                     .GetMethods()
                     .Where(m => m.Name == "Lambda")
                     .Select(m => new
                     {
                         Method = m,
                         Params = m.GetParameters(),
                         Args = m.GetGenericArguments()
                     })
                     .Where(x => x.Params.Length == 2
                                 && x.Args.Length == 1
                                 )
                     .Select(x => x.Method)
                     .First();
    }
}

Usage -

var expression = testObject.Build("YourPropertyName");

Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.




回答2:


As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):

static LambdaExpression MakeSelector(Type objectType, string path)
{
    var item = Expression.Parameter(objectType, "item");
    var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
    return Expression.Lambda(body, item);
}

But then you'll need to find a way to call your generic library method - using reflection or dynamic call.




回答3:


If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:

public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
    string property_name)
{
    var parameter = Expression.Parameter(typeof (TObject));

    return Expression.Lambda<Func<TObject, TPropertyType>>(
        Expression.Property(parameter, property_name), parameter);
}



回答4:


There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:

   public class IndexViewModel
   {
      public bool HasPassword { get; set; }
      public string PhoneNumber { get; set; }
      public bool TwoFactor { get; set; }
      public bool BrowserRemembered { get; set; }
    }

    //...........

    Expression<Func<IndexViewModel, bool>> ex =
    System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
        var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
        var res = ex.Compile()(model);
        // res == true
        System.Diagnostics.Debug.Assert(res);


来源:https://stackoverflow.com/questions/35342955/dynamically-creating-an-expression-which-selects-an-objects-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!