C # Как преобразовать выражение<Func<SomeType>> в выражение<Func<OtherType>>

StackOverflow https://stackoverflow.com/questions/654153

Вопрос

Я и раньше использовал выражения 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-команду.Преобразование выражения намного сложнее, потому что оно создано как неизменяемое и может вызвать нежелательные эффекты во время выполнения, если вы сделаете что-то неправильно, и, по крайней мере, в моем случае, это не требуется.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top