C # Как преобразовать выражение <Func <SomeType>> в выражение <Func <OtherType>> - PullRequest
15 голосов
/ 17 марта 2009

Ранее я использовал выражения на C #, основанные на лямдах, но у меня нет опыта их составления вручную. Учитывая Expression<Func<SomeType, bool>> originalPredicate, я хочу создать Expression<Func<OtherType, bool>> translatedPredicate.

В этом случае SomeType и OtherType имеют одинаковые поля, но они не связаны (без наследования и не основаны на общем интерфейсе).

Справочная информация: у меня есть реализация репозитория, основанная на LINQ to SQL. Я проецирую сущности LINQ to SQL на мои сущности Model, чтобы сохранить мою модель в POCO. Я хочу передать выражения в хранилище (как форму спецификаций), но они должны быть основаны на объектах модели. Но я не могу передать эти выражения в контекст данных, поскольку он ожидает выражения на основе сущностей LINQ to SQL.

Ответы [ 4 ]

21 голосов
/ 17 марта 2009

С 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 это нравится.

Другой вариант - полностью перестроить дерево выражений , используя отражение для поиска соответствующих членов. Гораздо сложнее.

3 голосов
/ 24 апреля 2012

Я нашел еще один способ, который также включает перенос вашего исходного делегата.

Func<T, object> ExpressionConversion<U>(Expression<Func<T, U>> expression)
{
    Expression<Func<T, object>> g = obj => expression.Compile().Invoke(obj);
    return g.Compile();
}
2 голосов
/ 17 марта 2009

Не существует неявного способа сделать перевод. Вы должны обернуть существующий делегат внутри лямбды, которая создает новый тип из типа аргумента:

var translatedPredicate = x => originalPredicate(OtherTypeFromSomeType(x))

Где OtherTypeFromSomeType создает экземпляр OtherType из аргумента SomeType.

0 голосов
/ 10 мая 2017

У меня была та же проблема, что и у вас, и я исправил ее так с помощью EF:

var viewModeValue = dbContext.Model.Select(m => new ViewModel{Field = m.Field}).Where(predicate) //predicate is an Expression<Func<ViewModel, bool>>

Entity Framework знает, как создать правильную команду sql. Преобразование выражения намного сложнее, потому что оно построено так, чтобы быть неизменным и могло вызвать нежелательные эффекты времени выполнения, если вы делаете что-то не так, и, по крайней мере, в моем случае, это не нужно.

...