C # Como converter um Expression > para um Expression >
-
19-08-2019 - |
Pergunta
Eu usei # expressões C antes baseado em lamdas, mas eu não tenho experiência compondo-los à mão. Dado um Expression<Func<SomeType, bool>> originalPredicate
, eu quero criar um Expression<Func<OtherType, bool>> translatedPredicate
.
Neste caso SomeType e OtherType têm os mesmos campos, mas eles não estão relacionados (sem herança e não com base em uma interface comum).
Fundo: Eu tenho uma implementação repositório baseado em LINQ to SQL. I projetar o LINQ to Entities SQL para meus entidades modelo, para manter o meu modelo em POCO. Eu quero passar expressões para o repositório (como uma forma de especificações), mas eles devem basear-se nas entidades do modelo. Mas não posso passar essas expressões para o contexto de dados, uma vez que espera expressões baseado no LINQ to Entities SQL.
Solução
Com Expression
, a maneira mais simples é com uma conversão expressão :
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 });
}
}
Note, porém, que esta será apoiado de forma diferente por diferentes fornecedores. EF pode não gostar dele, por exemplo, mesmo que LINQ to SQL faz.
A outra opção é para reconstruir a árvore de expressão completamente , usando a reflexão para encontrar os membros correspondentes. Muito mais complexa.
Outras dicas
Há uma outra maneira que eu encontrei, que também inclui envolvendo o delegado originais.
Func<T, object> ExpressionConversion<U>(Expression<Func<T, U>> expression)
{
Expression<Func<T, object>> g = obj => expression.Compile().Invoke(obj);
return g.Compile();
}
Não há nenhuma maneira implícita para fazer a tradução. Você tem que envolver seu delegado existente dentro de um lambda que cria um novo tipo do tipo de argumento:
var translatedPredicate = x => originalPredicate(OtherTypeFromSomeType(x))
Onde OtherTypeFromSomeType
cria a instância OtherType
a partir do argumento SomeType
.
Eu tive o mesmo problema que você e eu fixa-lo como esta com a EF:
var viewModeValue = dbContext.Model.Select(m => new ViewModel{Field = m.Field}).Where(predicate) //predicate is an Expression<Func<ViewModel, bool>>
Entity Framework sabe como construir o comando correto sql. Convertendo a expressão é muito mais complicado, porque ele é construído para ser imutável e pode causar efeitos indesejados tempo de execução se você fizer algo errado, e, no meu caso, pelo menos, ele não é necessário.