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

4

15

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);
Cytolysin answered 8/3, 2012 at 17:6 Comment(1)
You should show your code which isn't working...Americano
H
24
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});
Harhay answered 8/3, 2012 at 18:12 Comment(3)
Tried it, but getting The type or method has 1 generic parameter(s), but 2 generic argument(s) were provided. A generic argument must be provided for each generic parameter. error. StackTrace: at System.RuntimeType.SanityCheckGenericArguments(RuntimeType[] genericArguments, RuntimeType[] genericParamters) at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation) at Here_is_this_code(DbModelBuilder modelBuilder)Cytolysin
When I commented .MakeGenericMethod(...) I got Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true..Cytolysin
It turned out that funcType was not necessary.Cytolysin
S
3

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.

Scummy answered 8/3, 2012 at 17:41 Comment(0)
R
2

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.

Ryeland answered 8/3, 2012 at 17:22 Comment(0)
M
1

This code will get you an Expression<Func<>> of the desired type. Note that there is an Expression.Lambda(...) override that doesn't need you to specify the type of the Func returned.

    var t = typeof(Foo);
    var pi = t.GetProperty(...);
    var prm = Expression.Parameter(t, t.Name);
    var prx = Expression.Property(prm, pi);
    var lambda = Expression.Lambda(prx, prm);

Note that in many cases you don't have to bother with creating the Expression<Func<>> this way, assuming structureConfiguration below is a StructureConfiguration<Foo>, type inference will allow you to write something like this:

    structureConfiguration.Ignore(f => f.Bar);
Musa answered 13/1, 2022 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.