С# Как преобразовать выражение <Func <SomeType> в выражение <Func <OtherType>>
Я использовал выражения С# раньше, чем на основе lamdas, но у меня нет опыта составлять их вручную. Учитывая Expression<Func<SomeType, bool>> originalPredicate
, я хочу создать Expression<Func<OtherType, bool>> translatedPredicate
.
В этом случае SomeType и OtherType имеют одинаковые поля, но они не связаны (нет наследования и не основаны на общем интерфейсе).
Справочная информация. У меня есть реализация репозитория на основе LINQ to SQL. Я проектирую объекты LINQ to SQL для своих объектов модели, чтобы сохранить мою модель в POCO. Я хочу передать выражения в репозиторий (как форму спецификации), но они должны основываться на объектах модели. Но я не могу передать эти выражения в контексте данных, так как он ожидает выражения, основанные на объектах LINQ to SQL.
Ответы
Ответ 1
С Expression
самый простой способ - преобразование выражение:
class Foo {
public int Value { get; set; }
}
class Bar {
public int Value { get; set; }
}
static class Program {
static void Main() {
Expression<Func<Foo, bool>> predicate =
x => x.Value % 2 == 0;
Expression<Func<Bar, Foo>> convert =
bar => new Foo { Value = bar.Value };
var param = Expression.Parameter(typeof(Bar), "bar");
var body = Expression.Invoke(predicate,
Expression.Invoke(convert, param));
var lambda = Expression.Lambda<Func<Bar, bool>>(body, param);
// test with LINQ-to-Objects for simplicity
var func = lambda.Compile();
bool withOdd = func(new Bar { Value = 7 }),
withEven = func(new Bar { Value = 12 });
}
}
Обратите внимание, что это будет поддерживаться разными поставщиками. EF может не понравиться, например, даже если LINQ-to-SQL делает.
Другой вариант - перестроить дерево выражений полностью, используя отражение, чтобы найти соответствующие элементы. Гораздо сложнее.
Ответ 2
Есть еще один способ, который я нашел, который также включает в себя перенос оригинального делегата.
Func<T, object> ExpressionConversion<U>(Expression<Func<T, U>> expression)
{
Expression<Func<T, object>> g = obj => expression.Compile().Invoke(obj);
return g.Compile();
}
Ответ 3
Существует неявный способ сделать перевод. Вы должны обернуть существующий делегат внутри лямбда, который создает новый тип из типа аргумента:
var translatedPredicate = x => originalPredicate(OtherTypeFromSomeType(x))
Где OtherTypeFromSomeType
создает экземпляр OtherType
из аргумента SomeType
.
Ответ 4
У меня была та же проблема, что и вы, и я исправил ее с помощью EF:
var viewModeValue = dbContext.Model.Select(m => new ViewModel{Field = m.Field}).Where(predicate) //predicate is an Expression<Func<ViewModel, bool>>
Entity Framework знает, как создать правильную команду sql. Преобразование выражения намного сложнее, потому что оно построено как неизменное и может вызвать нежелательные эффекты времени выполнения, если вы делаете что-то неправильно и, по крайней мере, в моем случае, это не нужно.