Как преобразовать PropertyInfo в выражение свойства и использовать его для вызова универсального метода?

Как преобразовать PropertyInfo в выражение свойства, которое можно использовать для вызова StructuralTypeConfiguration<TStructuralType>.Ignore<TProperty>(Expression<Func<TStructuralType, TProperty>> propertyExpression)?

Я попытался использовать Expression.Property() для создания выражения, но я получаю следующую ошибку, когда я использую это выражение как параметр propertyExpression:

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

Эта ошибка, вероятно, относится к параметру типа TProperty, который я не знаю, как указать только PropertyInfo.

Я делаю это по отношению к: Используйте Entity Framework StructuralTypeConfiguration.Ignore(), чтобы игнорировать все свойства, но указанный набор.

UPDATE

Код, который не работает:

        var propertyInfo = typeof(Foo).GetProperties()[0];
        var expression = Expression.Default(typeof(Foo));
        var expressionProperty = Expression.Property(expression, propertyInfo);
        Ignore(expressionProperty);

Ответ 1

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

Ответ 2

Выражения свойства требуют, чтобы доступ к свойствам находился на определенном объекте. Здесь есть несколько вариантов. Во-первых, если это выполняется в одном из объектов сущности, вы можете просто использовать ConstantExpression для создания выражения свойства:

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

Однако, поскольку вам нужен Expression<Func<TStructuralType, TProperty>>, тогда вам кажется, что вам нужно будет создать его с помощью ParameterExpression:

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

ОДНАКО, здесь кикер... Это всего лишь MemberExpression. Чтобы преобразовать в нужное выражение, вам нужно использовать Expression.Lambda, чтобы получить выражение Func < > типа, который вам нужен. Проблема? Вы не знаете тип свойства для определения общих параметров выражения лямбда!

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

В этом суть проблемы этого. Это не значит, что это невозможно сделать... Просто использование этого метода в ЭТОМ ПУТЕ не сработает. Вам нужно будет использовать немного времени выполнения и статическую типизацию (а также разумное использование Actions вместо Funcs), чтобы заставить это работать правильно.

Ответ 3

TProperty существует только в тексте исходного кода С#. Компилятор всегда разрешает его конкретный тип. Если у вас есть метод

void Test<T>(T arg)
{
}

и назовите его следующим образом

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

Компилятор генерирует код для двух методов!

void Test(string arg)
{
}

void Test(int arg)
{
}

Это означает, что вам нужно предоставить конкретные типы для ваших общих параметров, если вы хотите использовать invokable метод.