c# - una - lambda sintaxis
Agregar argumento de Expresión como propiedad en LINQ a Entidades (1)
Lo que necesita aquí es un método para combinar varias expresiones. Específicamente, lo que nos gustaría es una forma de tomar una expresión que mapea un valor y luego también aceptar una expresión que acepte la entrada de la primera expresión y la salida de la primera expresión, y que calcule un nuevo valor.
Como una implementación de este método, podemos reemplazar todas las instancias de "el resultado de la primera función" con el cuerpo de la primera función; después de eso, todo lo que se necesita hacer es garantizar que ambas expresiones utilicen la misma instancia de Parameter
.
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
El siguiente código se usa para reemplazar todas las instancias de una expresión con otra:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
En cuanto a usar la función; es lo suficientemente simple Llamamos Combine
en su textExpression
, y luego podemos crear un lambda que acepte los resultados de fila y texto de la primera expresión como parámetros. Esto le permite escribir una lambda que es casi exactamente igual a la que ya tiene, pero donde puede usar el parámetro de texto para asignar el valor de Text
:
public IEnumerable<RowModel> GetRowModels(
Expression<Func<Row, string>> textExpr)
{
return MyDatabaseContext.MyTable.Select(
textExpr.Combine((row, text) => new RowModel
{
RowID = row.ID,
CreatedDate = row.CreatedDate,
AnotherProperty = row.AnotherProperty,
Text = text, // how do I bind this expression?
Value = row.OtherStuff.Where(os => os.ShouldUse)
.Select(os => os.Value).FirstOrDefault(),
AnotherValue = row.OtherStuff.Where(os => os.ShouldUseAgain)
.Select(os => os.Value).FirstOrDefault()
}));
}
Usando EF6, ¿cómo vincularía un argumento determinado de Expression<Func<Row, string>>
a una expresión de selección existente, sin tener que reescribir cada enlace de propiedad utilizando árboles de expresión?
public IEnumerable<RowModel> GetRowModels(Expression<Func<Row, string>> textExpr)
{
return from row in MyDatabaseContext.MyTable
select new RowModel
{
RowID = row.ID,
CreatedDate = row.CreatedDate,
AnotherProperty = row.AnotherProperty,
Text = textExpr, // how do I bind this expression?
Value = row.OtherStuff.Where(os => os.ShouldUse).Select(os => os.Value).FirstOrDefault(),
AnotherValue = row.OtherStuff.Where(os => os.ShouldUseAgain).Select(os => os.Value).FirstOrDefault()
};
}