Динамическое выражение члена - PullRequest
8 голосов
/ 16 марта 2011

Я хочу создать MemberExpression, зная только имя поля;Например:

public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>(string fieldName)
    {
        PropertyInfo fieldPropertyInfo;

        fieldPropertyInfo = typeof(TModel).GetProperty(fieldName);

        var entityParam = Expression.Parameter(typeof(TModel), "e"); // {e}
        var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
        var lambda = Expression.Lambda(columnExpr, entityParam) as Expression<Func<TModel, T>>; // {e => e.column}

        return lambda;
    }

Проблема с вышесказанным в том, что тип поля должен быть строго типизирован.Передача «объекта» в качестве типа поля не работает.Есть ли способ создать это?Даже Dynamic LINQ не работает.

Ответы [ 2 ]

18 голосов
/ 16 марта 2011

Существует ряд проблем с вашим кодом:

  1. Параметр вашего метода называется fieldName, но вы получаете свойство с ним.
  2. Вы используете неуниверсальный метод Expression.Lambda для генерации выражения, которое может выбрать неподходящий тип делегата, если аргумент типа T, переданный методу, не совпадает с типом свойства. В этом случае приведение as из выражения к типу возврата метода завершится ошибкой и оценивается как null. Решение: Используйте универсальный Lambda метод с соответствующими аргументами типа. Кастинг не требуется.
  3. Если вы решите вторую проблему, все будет работать нормально, если доступно безопасное преобразование ссылок из типа свойства в T, но не тогда, когда требуются более сложные преобразования, такие как бокс / лифтинг. Решение: При необходимости используйте метод Expression.Convert.

Вот обновление вашего примера, которое решает эти проблемы:

public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>
   (string propertyName)
{
    var propertyInfo = typeof(TModel).GetProperty(propertyName);

    var entityParam = Expression.Parameter(typeof(TModel), "e"); 
    Expression columnExpr = Expression.Property(entityParam, propertyInfo);

    if (propertyInfo.PropertyType != typeof(T))
        columnExpr = Expression.Convert(columnExpr, typeof(T));

    return Expression.Lambda<Func<TModel, T>>(columnExpr, entityParam);
}

Это сделает все следующие вызовы успешными:

GenerateMemberExpression<FileInfo, string>("Name");
GenerateMemberExpression<string, int>("Length");

// Reference conversion
GenerateMemberExpression<FileInfo, object>("Name");          

//Boxing conversion
GenerateMemberExpression<string, object>("Length");

//Lifted conversion
GenerateMemberExpression<string, int?>("Length");
2 голосов
/ 16 марта 2011

Попробуйте вручную преобразовать значение поля в случае передачи «объекта». Например:

var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
if (T.GetType().Equals(typeof(object)))
{
    columnExpr = Expression.Convert(columnExpr, typeof(object));
}

Надеюсь, это поможет вам.

...