Существует ряд проблем с вашим кодом:
- Параметр вашего метода называется
fieldName
, но вы получаете свойство с ним.
- Вы используете неуниверсальный метод
Expression.Lambda
для генерации выражения, которое может выбрать неподходящий тип делегата, если аргумент типа T
, переданный методу, не совпадает с типом свойства. В этом случае приведение as
из выражения к типу возврата метода завершится ошибкой и оценивается как null
. Решение: Используйте универсальный Lambda
метод с соответствующими аргументами типа. Кастинг не требуется.
- Если вы решите вторую проблему, все будет работать нормально, если доступно безопасное преобразование ссылок из типа свойства в
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");