Aquí es una solución utilizando AutoMapper:
Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
ACTUALIZACIÓN: Ha sido de 1,5 años desde la primera vez respondí esto, y yo pensé que podría expandir en mi respuesta ahora ya que las personas se preguntan cómo hacer esto cuando tienes una expresión en oposición a un delegado.
La solución es la misma en principio: necesitamos poder compose las dos funciones (selector
y mapper
) en una sola función. Desafortunadamente, dado que no hay forma de que C# "llame" una expresión de otra (como podríamos hacerlo con los delegados), no podemos representar esto directamente en el código. Por ejemplo, el siguiente código fallará para compilar:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
La única manera de crear nuestra función compuesta, por lo tanto, es la construcción de la expression tree a nosotros mismos utilizando los System.Linq.Expressions
clases.
Lo que realmente tenemos que hacer es modificar el cuerpo de la función selector
para que todas las instancias de su parámetro sean reemplazadas por el cuerpo de la función mapper
. Esto se convertirá en el cuerpo de nuestra nueva función, que aceptará el parámetro mapper
.
Para reemplazar el parámetro he creado una subclase de ExpressionVisitor clase que puede atravesar un árbol de expresión y reemplazar un solo parámetro con una expresión arbitraria:
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _parameter;
private Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}
Entonces creé un método de extensión, Compose()
, que utiliza la visitante a componer dos expresiones lambda, un exterior y un interior:
public static class FunctionCompositionExtensions
{
public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
{
return Expression.Lambda<Func<X ,Y>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}
Ahora, con todo lo que la infraestructura en su lugar, podemos modificar nuestro método GetMappedSelector()
utilizar nuestro Compose()
extensión:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
return mappedSelector;
}
que creó un simple console application a probar esto. Con suerte, mi explicación no estaba demasiado ofuscada; pero desafortunadamente, no hay un enfoque más simple para hacer lo que estás tratando de hacer. Si todavía estás totalmente confundido, al menos puedes reutilizar mi código y has apreciado los matices y las complejidades de tratar con los árboles de expresiones.
Qué pasa si tengo algo como: pública IEnumerable (Expresión > { var x = dbo.Products.Get (ExpressionMapper.GetMappedSelector (predicate);) } solo funciona con Func no con Expression > –
@ persianDev podría encontrar una manera de hacer que esto funcione? yo tengo el mismo problema. –
@bahadirarslan Actualicé mi respuesta para abordar este problema. – luksan