C # Как преобразовать выражение<Func<SomeType>> в выражение<Func<OtherType>>
-
19-08-2019 - |
Вопрос
Я и раньше использовал выражения C #, основанные на lamdas, но у меня нет опыта составления их вручную.Учитывая Expression<Func<SomeType, bool>> originalPredicate
, Я хочу создать Expression<Func<OtherType, bool>> translatedPredicate
.
В этом случае SomeType и OtherType имеют одинаковые поля, но они не связаны (нет наследования и не основаны на общем интерфейсе).
Предыстория:У меня есть реализация репозитория, основанная на LINQ to SQL.Я проецирую объекты LINQ to SQL на объекты моей модели, чтобы сохранить мою модель в POCO.Я хочу передать выражения в репозиторий (в виде спецификаций), но они должны быть основаны на объектах модели.Но я не могу передать эти выражения в контекст данных, поскольку он ожидает выражения, основанные на объектах LINQ to SQL.
Решение
С 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 понравится.
Другой вариант - перестроить дерево выражений полностью, используя отражение для поиска соответствующих членов.Гораздо сложнее.
Другие советы
Есть еще один способ, который я нашел, который также включает в себя перенос вашего исходного делегата.
Func<T, object> ExpressionConversion<U>(Expression<Func<T, U>> expression)
{
Expression<Func<T, object>> g = obj => expression.Compile().Invoke(obj);
return g.Compile();
}
Не существует неявного способа выполнить перевод.Вы должны обернуть свой существующий делегат внутри лямбда-выражения, которое создает новый тип из типа аргумента:
var translatedPredicate = x => originalPredicate(OtherTypeFromSomeType(x))
Где OtherTypeFromSomeType
создает OtherType
экземпляр из SomeType
аргумент.
У меня была та же проблема, что и у вас, и я исправил ее следующим образом с помощью EF:
var viewModeValue = dbContext.Model.Select(m => new ViewModel{Field = m.Field}).Where(predicate) //predicate is an Expression<Func<ViewModel, bool>>
Entity Framework знает, как создать правильную sql-команду.Преобразование выражения намного сложнее, потому что оно создано как неизменяемое и может вызвать нежелательные эффекты во время выполнения, если вы сделаете что-то неправильно, и, по крайней мере, в моем случае, это не требуется.