Expression<Func<Entity, bool>> expr = e => e.Propriedade == "Valor";
Deve ser convertida para:
Expression<Func<Dto, bool>> expr = e => e.Propriedade == "Valor";
A solução envolve analisar a árvore de expresssão e substituir os nós com base no mapeamento. Abaixo está uma implementação genérica para essa conversão.
Impelmentação da Extensão
Uma classe estática de extensão é criada para lidar com a composição de expressões, incluindo cache para desempenho.
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper;
using AutoMapper.QueryableExtensions;
namespace Expressoes.Core
{
public static class ConversorExpressoesExtensions
{
private static readonly ConcurrentDictionary<ChaveTipo, TuplaMetodoExpressao> CacheExpressoes =
new ConcurrentDictionary<ChaveTipo, TuplaMetodoExpressao>();
private static readonly MethodInfo MetodoComposicao = typeof(ConversorExpressoesExtensions)
.GetMethod("Compor", BindingFlags.NonPublic | BindingFlags.Static);
public static Expression<Func<TAlvo, bool>> ConverterExpressao<TOrigem, TAlvo>(
this Expression<Func<TOrigem, bool>> expressaoOrigem)
{
var chave = new ChaveTipo(typeof(TOrigem), typeof(TAlvo));
var tupla = CacheExpressoes.GetOrAdd(chave, _ =>
{
var expressaoMapeamento = Mapper.Engine.CreateMapExpression<TAlvo, TOrigem>();
var metodo = MetodoComposicao.MakeGenericMethod(typeof(TAlvo), typeof(bool), typeof(TOrigem));
return new TuplaMetodoExpressao(metodo, expressaoMapeamento);
});
return tupla.Metodo.Invoke(null, new object[] { expressaoOrigem, tupla.Expressao }) as
Expression<Func<TAlvo, bool>>;
}
private static Expression<Func<TX, TY>> Compor<TX, TY, TZ>(
Expression<Func<TZ, TY>> expressaoExterna, Expression<Func<TX, TZ>> expressaoInterna)
{
var corpoSubstituido = VisitanteExpressaoHelper.Substituir(
expressaoExterna.Body, expressaoExterna.Parameters[0], expressaoInterna.Body);
return Expression.Lambda<Func<TX, TY>>(corpoSubstituido, expressaoInterna.Parameters[0]);
}
private class ChaveTipo
{
public Type TipoOrigem { get; }
public Type TipoAlvo { get; }
public ChaveTipo(Type tipoOrigem, Type tipoAlvo)
{
TipoOrigem = tipoOrigem;
TipoAlvo = tipoAlvo;
}
public override int GetHashCode() => TipoOrigem.GetHashCode() ^ TipoAlvo.GetHashCode();
public override bool Equals(object obj) =>
obj is ChaveTipo outro && TipoOrigem == outro.TipoOrigem && TipoAlvo == outro.TipoAlvo;
}
private class TuplaMetodoExpressao
{
public MethodInfo Metodo { get; }
public Expression Expressao { get; }
public TuplaMetodoExpressao(MethodInfo metodo, Expression expressao)
{
Metodo = metodo;
Expressao = expressao;
}
}
}
internal class VisitanteExpressaoHelper : ExpressionVisitor
{
private readonly ParameterExpression _parametroOriginal;
private readonly Expression _substituto;
private VisitanteExpressaoHelper(ParameterExpression parametroOriginal, Expression substituto)
{
_parametroOriginal = parametroOriginal;
_substituto = substituto;
}
public static Expression Substituir(Expression expressao, ParameterExpression parametroOriginal, Expression substituto)
{
return new VisitanteExpressaoHelper(parametroOriginal, substituto).Visit(expressao);
}
protected override Expression VisitParameter(ParameterExpression no)
{
return no == _parametroOriginal ? _substituto : base.VisitParameter(no);
}
}
}
Exemplo de Uso
Configure o AutoMapper e utilize a extensão para converter expressões entre tipos.
Mapper.CreateMap<Entidade, Dto>();
Mapper.CreateMap<Dto, Entidade>();
var lista = new List<Dto>
{
new Dto { Nome = "Teste" },
new Dto { Nome = "Exemplo" },
new Dto { Nome = "Outro" }
};
Expression<Func<Entidade, bool>> expressaoEntidade = e => e.Nome == "Exemplo";
var expressaoDto = expressaoEntidade.ConverterExpressao<Entidade, Dto>();
var resultado = lista.Where(expressaoDto.Compile());
// resultado contém um elemento correspondente