Fusión de expresión lambda .net

La causa del incidente fue que un joven de la empresa me hizo una pregunta "Hermano Hai, ayúdame a ver por qué este código no funciona"

Func<Report,bool> nameFilter = x=>x.Name == "test";
DbContext.Report.Where(x=>x.State==1 && nameFilter(x));

Lo vi, buen chico, puedes pensar en un código tan coqueto. En circunstancias normales, puedes hacer esto con Linq To Object, pero no puedes hacer esto con consultas EF IQueryable.
Linq To Object ejecuta directamente la expresión. Es un método de delegado. Se puede ejecutar directamente cuántas capas de delegados y métodos están anidados en él. No ejecuta
IQueryableexpresiones y métodos, pero convierte expresiones en declaraciones Sql correspondientes para su ejecución. , cuando analizó el nameFilter, estaba confundido. ¿Qué diablos es esto? No existe tal cosa en sql, por lo que no puede convertirlo.

Después de que el joven se enteró, Mingxi estaba muy decepcionado. Eso no se puede hacer, y no es que quiera mostrar mis habilidades, pero solo quiero que el joven continúe con sus llamativas operaciones y le dé algo de tecnología hexadecimal y trabajo duro.

solución:

//表达式
Func<Report,bool> nameFilter = x=>x.Name == "test";
Func<Report,bool> stateFilter = x=>x.State==1;
//合并为
Func<Report,bool> whereFilter = x=>x.Name == "test" && x.State==1;

//调用
DbContext.Report.Where(whereFilter);

Solución perfecta

Luego, cómo fusionar, por supuesto, debe construir una nueva expresión usted mismo. ExpressionSe necesitan clases para construir expresiones. Si no ha usado esta clase, puede depurarla de la siguiente manera para ver cómo una expresión se convierte en una expresión. árbol. .


TestExpression(x=>x.Name == "test",x=>x.State==1);

public static void TestExpression(Expression<Func<Report, bool>> left,Expression<Func<Report, bool>> right)
{ 
    //调试查看expression对象
    var bodyLeft = left.Body;//这个就是x.Name == "test"
    var bodyRight = right.Body;//这个就是x.State==1
}

Bien, aquí podemos obtener el cuerpo de la expresión y luego usar Expressionla clase para fusionar bien los cuerpos de las dos expresiones.

var andAlso = Expression.AndAlso(bodyLeft ,bodyRight);//x.Name == "test" && x.State==1

Esto no es suficiente, estas dos expresiones son dos objetos delegados diferentes, y su parámetro x también son dos objetos diferentes, combinados pero no completamente combinados.

Esto requiere el uso de ExpressionVisitorclases para repetir el árbol de expresiones y reemplazar los parámetros de las dos expresiones con el mismo parámetro.

    /// <summary>
    /// 替换表达式参数
    /// </summary>
    public class ReplaceExpressionVisitor : ExpressionVisitor
    {
        private Expression _leftParameter;

        public ReplaceExpressionVisitor(Expression leftParameter)
        {
            _leftParameter= leftParameter;
        }
        
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return _leftParameter;
        }
    }

finalmente


TestExpression(x=>x.Name == "test",x=>x.State==1);

public static void TestExpression(Expression<Func<Report, bool>> left,Expression<Func<Report, bool>> right)
{ 
    //调试查看expression对象
    var bodyLeft = left.Body;//这个就是x.Name == "test"
    var bodyRight = right.Body;//这个就是x.State==1
    var leftParameter = left.Parameters[0];
    //表达式递归访问
    var visitor =new ReplaceExpressionVisitor(leftParameter);
    //替换参数
    bodyRight = visitor.Visit(bodyRight);
    //合并表达式
    var expression = Expression.AndAlso(bodyLeft , bodyRight);
    //构建表达式
    var whereExpression= Expression.Lambda<Func<Report, bool>>(expression , left.Parameters);
    //编译表达式
    var whereFilter = whereExpression.Compile();
    //使用
    DbContext.Report.Where(whereFilter);
}

Cuando estaba a punto de presumirle al hermanito, se fue a escribir otros códigos vistosos

No es un show, pero es un show, mejoremos la lista, el siguiente es el código completo

Los amigos que no quieran mover las manos pueden buscar directamente en nuget DynamicExpression.Corey usarlo directamente

Para obtener más código fuente, consulte mi github

    /// <summary>
    /// 替换表达式参数
    /// </summary>
    public class ReplaceExpressionVisitor : ExpressionVisitor
    {
        private Dictionary<Expression, Expression> _parameters;

        public ReplaceExpressionVisitor(Dictionary<Expression,Expression> parameters)
        {
            _parameters = parameters;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_parameters.TryGetValue(node, out Expression _newValue))
            {
                return _newValue;
            }
            return base.Visit(node);
        }
    }
    /// <summary>
    /// 表达式扩展
    /// </summary>
    public static class ExpressionExtension
    {

        /// <summary>
        /// 使用AndAlso合并表达式
        /// </summary>
        /// <param name="exprs"></param>
        /// <returns></returns>
        public static Expression<T> AndAlso<T>(this IList<Expression<T>> exprs)
        {
            if (exprs.Count == 0) return null;
            if (exprs.Count == 1) return exprs[0];

            var leftExpr = exprs[0];
            var left = leftExpr.Body;
            for (int i = 1; i < exprs.Count; i++)
            {
                var expr = exprs[i];
                var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
                var right = visitor.Visit(expr.Body);
                left = Expression.AndAlso(left, right);
            }
            return Expression.Lambda<T>(left, leftExpr.Parameters);
        }

        /// <summary>
        /// 使用AndAlso合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns>left AndAlso right</returns>
        public static Expression<T> AndAlso<T>(this Expression<T> left, Expression<T> right)
        {
            return AndAlso(new List<Expression<T>>() { left, right });
        }

        /// <summary>
        /// 使用OrElse合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exprs"></param>
        /// <returns></returns>
        public static Expression<T> OrElse<T>(this IList<Expression<T>> exprs)
        {
            if (exprs.Count == 0) return null;
            if (exprs.Count == 1) return exprs[0];

            var leftExpr = exprs[0];
            var left = leftExpr.Body;
            for (int i = 1; i < exprs.Count; i++)
            {
                var expr = exprs[i];
                var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
                var right = visitor.Visit(expr.Body);
                left = Expression.OrElse(left, right);
            }
            return Expression.Lambda<T>(left, leftExpr.Parameters);
        }

        /// <summary>
        /// 使用OrElse合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns>left OrElse right</returns>
        public static Expression<T> OrElse<T>(this Expression<T> left, Expression<T> right)
        {
            return OrElse(new List<Expression<T>>() { left, right });
        }


        /// <summary>
        /// 构建visitor
        /// </summary>
        /// <param name="oldParameters"></param>
        /// <param name="newParameters"></param>
        /// <returns></returns>
        private static ReplaceExpressionVisitor GetReplaceExpressionVisitor(ReadOnlyCollection<ParameterExpression> oldParameters, ReadOnlyCollection<ParameterExpression> newParameters)
        {
            Dictionary<Expression, Expression> dic = new Dictionary<Expression, Expression>();
            for (int i = 0; i < oldParameters.Count; i++)
            {
                dic.Add(oldParameters[i],newParameters[i]);
            }
            return new ReplaceExpressionVisitor(dic);
        }
    }

usar

string connectString = "Data Source=.;Initial Catalog=RportTest;Integrated Security=True";
var optionsBuilder = new DbContextOptionsBuilder<TestContext>();
optionsBuilder.UseSqlServer(connectString);
using (TestContext ctx = new TestContext(optionsBuilder.Options))
{

    Expression<Func<ReportData, bool>> epxr1 = report => report.ID == 2023;
    Expression<Func<ReportData, bool>> epxr2 = report => report.Name == "test1";

    var epxr3 = new List<Expression<Func<ReportData, bool>>>() { epxr1, epxr2 };

    var andPredicate = epxr3.AndAlso();
    var andQuery = ctx.ReportData.Where(andPredicate);
    string andSql = andQuery.ToQueryString();
    var andResult = andQuery.ToList();

    var orPredicate = epxr3.OrElse();
    var orQuery = ctx.ReportData.Where(orPredicate);
    string orSql = orQuery.ToQueryString();
    var orResult = orQuery.ToList();
}

Supongo que te gusta

Origin blog.csdn.net/weixin_47367099/article/details/127536474
Recomendado
Clasificación