Expression Property by string - c#

I haven't worked with expressions that much, I am trying to reference an Expression property by string name but I cam getting this error:
c# The member expression must specify a property or method that is
public and that belongs to the type Soly.Models.Profile (Parameter
'expression')
public class ProfileFilterType : FilterInputType<Profile> {
protected override void Configure(
IFilterInputTypeDescriptor<Profile> descriptor) {
descriptor.BindFieldsExplicitly();
descriptor.Field(f => Build<IFilterInputTypeDescriptor<Profile>, string>("firstName"));
}
public static Expression<Func<TClass, TProperty>> Build<TClass, TProperty>(string fieldName) {
var param = Expression.Parameter(typeof(TClass));
var field = Expression.PropertyOrField(param, fieldName);
return Expression.Lambda<Func<TClass, TProperty>>(field, param);
}
}
descriptor.field signature:
IFilterFieldDescriptor Field<TField>(Expression<Func<T, TField>> propertyOrMember);
I am trying to iterate over the Profile properties with reflection and add a field descriptor for each in HotChocolate GraphQL.

Change descriptor.Field(f => Build<IFilterInputTypeDescriptor<Profile>, string>("firstName")); to:
descriptor.Field(Build<Profile, string>("firstName"));
Otherwise your are creating an expression with following type Expression<Func<Profile, Expression<Func<IFilterInputTypeDescriptor<Profile>,string>>>> which is definitely not what is expected.
P.S.
Not sure, but should not something like descriptor.Field("firstName"); or descriptor.Field(p => p.firstName); just work without need to manually handle expression trees?

Related

C# Expression.Property of an interface

I was generating a dynamic expression on CustomType based on some parameters. Code looks like this:
ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
When I changed from type CustomType to interface ICustomType it stopped working by throwing an error 'Instance property 'CustomProperty' is not defined for type 'ICustomType''
. How to fix it?
Without a minimum verifiable example, we cannot be sure what the problem is, but from your example code, I've put together the following:
interface ICustomType
{
int CustomProperty { get; set; }
}
class CustomType : ICustomType
{
public int CustomProperty { get; set; }
}
Now, when I call your example code, everything works as expected
ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
Also, when I change the type to ICustomType, it still works as expected.
ParameterExpression parameter = Expression.Parameter(typeof(ICustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
However, if I remove the declaration of CustomProperty from ICustomType, I get the following error:
Instance property 'CustomProperty' is not defined for type 'ICustomType'
So, this leads me to believe that your interface does not include a declaration for CustomProperty. If you add it to your interface, your code should then work.
A common task when working with expressions is to replace certain nodes with other nodes. For instance, you can replace ParameterExpression as in this answer. I believe the OP used a similar parameter replacer, and forgot to also replace MemberExpression.
If you replace parameters of an expression, the original MemberExpression might not the new parameter type. E.g. a member expression for CustomType.CustomProperty will not be able to handle ICustomType.CustomProperty.
If my theory is correct, the OP must replace some MemberExpression instances too. The following expression visitor will do the trick:
public class ParameterReplacerVisitor : ExpressionVisitor
{
private readonly Type newType;
private Dictionary<ParameterExpression, ParameterExpression> parametersToReplace;
public ParameterReplacerVisitor(Type newType)
{
this.newType = newType;
}
public LambdaExpression Convert(LambdaExpression expression)
{
parametersToReplace = expression.Parameters
.Where(p => ShouldReplace(p.Type))
.ToDictionary(p => p, p => Expression.Parameter(newType, p.Name));
return (LambdaExpression)Visit(expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
var parameters = node.Parameters.Select(GetNewParameter);
return Expression.Lambda(Visit(node.Body), parameters);
}
protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(GetNewParameter(node));
}
protected override Expression VisitMember(MemberExpression node)
{
if (ShouldReplace(node.Member.DeclaringType))
{
var targetProperty = GetNewProperty(node.Member);
node = Expression.MakeMemberAccess(Visit(node.Expression), targetProperty);
}
return base.VisitMember(node);
}
private MemberInfo GetNewProperty(MemberInfo member)
{
return newType.GetProperty(member.Name) ?? throw new ArgumentException(
$"Property '{member.Name}' is not defined for type '{newType.Name}'"
);
}
private bool ShouldReplace(Type type) => newType.IsAssignableFrom(type);
private ParameterExpression GetNewParameter(ParameterExpression parameter)
{
parametersToReplace.TryGetValue(parameter, out var newParameter);
return newParameter ?? parameter;
}
}
Example
Expression<Func<Derived, string>> derived = t => t.A;
var lessDerived = derived.ToLessDerived<Derived, IBase, string>();
var d = lessDerived.Compile();
var result = d.Invoke(new Base());
And the extension method:
public static Expression<Func<TLess, TValue>> ToLessDerived<TClass, TLess, TValue>(this Expression<Func<TClass, TValue>> expression)
{
var visitor = new ParameterReplacerVisitor(typeof(TLess));
return (Expression<Func<TLess, TValue>>)visitor.Convert(expression);
}
For me, this solved the exact same type of error as the OP asked about.

Dynamically creating an expression which selects an objects property

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

Traverse an expression tree and extract parameters

I'm writing a kind of a mapping tool. I have a method that looks like this (simplified):
public void RegisterMapping<TTarget, TSource>(string propertyName,
Expression<Func<TSource, object>> memberMap)
The memberMap is an expression defining how to transform a property from TSource to TTarget. For the business logic, I need to extract all references to properties of TSource from it. For example, from
x => x.Customers.Where(c => c.Orders.Any())
I would like to get Customers, and from
x => x.FirstName + " " + x.LastName
FirstName and LastName (could be as string[], PropertyInfo is trivial to convert to).
How would I do this? My first approach was to traverse the tree manually, checking the node type and inspecting different properties depending on the node type (e.g. Operand for unary expressions, Arguments for a function call) to determine of any of these are a property of TSource. Then I discovered the expression kind list and I gave up -- even if I support only the most common kinds, it's still lots of work. Then I found the ExpressionVisitor. It looks better, but it's still a lot of work to override the visitor methods and I'd like to know if there's an other option, using perhaps a more specialised framework, before I devote my time to this.
I think as you've said that using ExpressionVisitor works out to be a good approach. You don't need to implement all the Visit... methods as they already have a default implementation. From what I understood what you want is to find all property accesses of a certain type inside a lambda function
public class MemberAccessVisitor : ExpressionVisitor
{
private readonly Type declaringType;
private IList<string> propertyNames = new List<string>();
public MemberAccessVisitor(Type declaringType)
{
this.declaringType = declaringType;
}
public IEnumerable<string> PropertyNames { get { return propertyNames; } }
public override Expression Visit(Expression expr)
{
if (expr != null && expr.NodeType == ExpressionType.MemberAccess)
{
var memberExpr = (MemberExpression)expr;
if (memberExpr.Member.DeclaringType == declaringType)
{
propertyNames.Add(memberExpr.Member.Name);
}
}
return base.Visit(expr);
}
}
This could be further improved to what you want by checking the member is a property and also to get PropertyInfo rather than strings
It could be used as follows:
var visitor = new MemberAccessVisitor(typeof(TSource));
visitor.Visit(memberMap);
var propertyNames = visitor.PropertyNames;

How to change a type in an expression tree?

I have a method like this:
private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
/* Some code that will call Method_2 */
}
In this method I want to change the IPerson type to another type. I want to call another method that looks like this:
private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
/* Some code */
}
So, in method_1 I need to change IPerson to PersonData. How can I do this?
Edit:
When I call: Method_1(p => p.Id == 1) I want to 'save' the condition (p.Id == 1) but I want to execute this condition on another type, namely IPerson. So, I need to alter the expression or create a new expression with IPerson
It is easy if you use .net 4 (update: as noted in comment ExpressionVisitor was added in version 4 not 4.5) it would require some googling for older frameworks:
There are some assumptions but I think they are valid for your DTO and Entity scenario - properties accessed must match.
class PersonData
{
public bool Prop { get; set; }
}
interface IPerson
{
bool Prop { get; set; }
}
In .net 4 there is ExpressionVisitor class defined that makes this a lot easier if you use older one then you need to write or find implementation of it:
class Visitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
//only properties are allowed if you use fields then you need to extend
// this method to handle them
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
//name of a member referenced in original expression in your
//sample Id in mine Prop
var memberName = node.Member.Name;
//find property on type T (=PersonData) by name
var otherMember = typeof(T).GetProperty(memberName);
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
}
Proof of concept:
class Program
{
static void Main()
{
//sample expression
Expression<Func<IPerson, bool>> expression = x => x.Prop;
//parameter that will be used in generated expression
var param = Expression.Parameter(typeof(PersonData));
//visiting body of original expression that gives us body of the new expression
var body = new Visitor<PersonData>(param).Visit(expression.Body);
//generating lambda expression form body and parameter
//notice that this is what you need to invoke the Method_2
Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
//compilation and execution of generated method just to prove that it works
var boolValue = lambda.Compile()(new PersonData());
}
}
Note that this will work for simple expressions. If you need to handle x.Prop.Prop1 < 3 then you need to extend this further.
This is an improvement of #Rafal solution. It allows to pipeline property calls of complex objects.
//sample expression
Expression<Func<IPerson, bool>> expression = x => x.PropA.PropB.PropC == "ABC";
Simply change the otherMember to use the type of inner.
class Visitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
//only properties are allowed if you use fields then you need to extend
// this method to handle them
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
//name of a member referenced in original expression in your
//sample Id in mine Prop
var memberName = node.Member.Name;
/*Fix*/
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
//find property on type T (=PersonData) by name
var otherMember = inner.Type.GetProperty(memberName);
return Expression.Property(inner, otherMember);
}
}

Get the name of a property by passing it to a method

StackOverflow user jolson had a very nice piece of code that exemplifies how one can register menthods without using strings, but expression trees here.
Is it possible to have something similar for properties instead of methods? To pass a property (not the name of the property) and inside the method to obtain the property name?
Something like this:
RegisterMethod(p => p.Name)
void RegisterMethod(Expression??? propertyExpression) where T : Property ???
{
string propName = propertyExpression.Name;
}
Thanks.
I posted a full example of this here (see also the post about "this" underneath it)
Note it deals with the LambdaExpression etc. As an update to the code as posted, you can add a bit more to make it easier to use in some scenarios:
static class MemberUtil<TType>
{
public static string MemberName<TResult>(Expression<Func<TType, TResult>> member)
{
return MemberUtil.MemberName<TType, TResult>(member);
}
}
Then you can use generic type-inference for the return value:
string test1 = MemberUtil<Foo>.MemberName(x => x.Bar);
string test2 = MemberUtil<Foo>.MemberName(x => x.Bloop());
You can write something along this:
static void RegisterMethod<TSelf, TProp> (Expression<Func<TSelf, TProp>> expression)
{
var member_expression = expression.Body as MemberExpression;
if (member_expression == null)
return;
var member = member_expression.Member;
if (member.MemberType != MemberTypes.Property)
return;
var property = member as PropertyInfo;
var name = property.Name;
// ...
}

Categories