Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert PropertyInfo to property expression and use it to invoke generic method?

How to convert PropertyInfo to property expression which can be used to invoke StructuralTypeConfiguration<TStructuralType>.Ignore<TProperty>(Expression<Func<TStructuralType, TProperty>> propertyExpression) method?

I tried to use Expression.Property() to construct expression but I am getting following error when I use this expression as propertyExpression parameter:

The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly.

This error probably refers to TProperty type parameter which I don't know how to specify having only PropertyInfo.

I am doing this in relation to: Use Entity Framework's StructuralTypeConfiguration.Ignore() to Ignore all properties but specified set.

UPDATE

Code which is not working:

var propertyInfo = typeof(Foo).GetProperties()[0];
var expression = Expression.Default(typeof(Foo));
var expressionProperty = Expression.Property(expression, propertyInfo);
Ignore(expressionProperty);
like image 278
Pol Avatar asked Mar 08 '12 17:03

Pol


3 Answers

var entityType = propertyInfo.DeclaringType;
var parameter = Expression.Parameter(entityType, "entity");
var property = Expression.Property(parameter, propertyInfo);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyInfo.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);

structureConfiguration.GetType()
   .GetMethod("Ignore")
   .MakeGenericMethod(propertyInfo.PropertyType)
   .Invoke(structureConfiguration, new[]{lambda});
like image 167
Serj-Tm Avatar answered Nov 10 '22 23:11

Serj-Tm


Property expressions require the property access to be on a specific object. There's a few options you can take here. First, if this is being done within one of your entity objects, you can simple use a ConstantExpression to build the property expression:

// Already have PropertyInfo in propInfo
Expression.Property(Expression.Constant(this, this.GetType()), propInfo)

However, since you need a Expression<Func<TStructuralType, TProperty>>, then it seems like you're going to have to build it using a ParameterExpression:

ParameterExpression pe = Parameter.Expression(typeof(MyEntity), "eParam");
Expression propExp = Expression.Property(pe, propInfo);

HOWEVER, here's the kicker... This is just a MemberExpression. To convert to the expression you need, you need to use Expression.Lambda to get a Func<> expression of the type you need. The problem? You don't know the type of the property to define the generic parameters of the lambda expression!

Expression<Func<MyEntity, ????>> eFunc = Expression.Lambda<Func<MyEntity, ????>>(propExp, pe);

This is the crux of the problem of doing it this way. That's not to say it can't be done... It's just that using this method IN THIS WAY isn't going to work. You'll have to use a bit runtime and static typing trickery (as well as judicious use of Actions instead of Funcs) to get this to work correctly.

like image 2
SPFiredrake Avatar answered Nov 10 '22 23:11

SPFiredrake


TProperty exists only in the c# source code text. The compiler always resolves it to a concrete type. If you have a method

void Test<T>(T arg)
{
}

and call it like this

Test("hello");
Test(3);

The compiler generates code for two methods!

void Test(string arg)
{
}

void Test(int arg)
{
}

This means that you have to supply concrete types for your generic parameters if you want to have an invokable method.

like image 1
Olivier Jacot-Descombes Avatar answered Nov 10 '22 23:11

Olivier Jacot-Descombes