fusão de expressão lambda .net

A causa do incidente foi que um jovem da empresa me fez uma pergunta "Irmão Hai, ajude-me a ver por que este código não está funcionando"

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

Eu vi, bom rapaz, você pode pensar em um código tão coquete. Em circunstâncias normais, você pode fazer isso com Linq To Object, mas não pode fazer isso com consultas EF IQueryable.
Linq To Object executa diretamente a expressão. É um método delegado. Quantas camadas de delegados e métodos estão aninhados nele podem ser executados diretamente. Ele não executa
IQueryableexpressões e métodos, mas converte expressões em declarações Sql correspondentes para execução. , quando ele analisou o nameFilter, ele ficou confuso.O que diabos é isso?Não existe tal coisa em sql, então ele não pode convertê-lo.

Depois que o jovem descobriu, Mingxi ficou muito desapontado. Isso não pode ser feito, e não é que eu queira mostrar minhas habilidades, mas só quero que o jovem continue suas operações vistosas e dê a ele alguma tecnologia hexadecimal e trabalho duro.

solução:

//表达式
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);

solução perfeita

Então, como mesclar, é claro, você mesmo deve construir uma nova expressão. ExpressionClasses são necessárias para construir expressões. Se você não usou esta classe, pode depurar da seguinte maneira para ver como uma expressão é convertida em uma árvore de expressão .


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
}

Ok, aqui podemos pegar o corpo da expressão, e então usar Expressiona classe para mesclar bem os corpos das duas expressões

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

Isso não é suficiente, essas duas expressões são dois objetos delegados diferentes e seu parâmetro x também é dois objetos diferentes, mesclados, mas não completamente mesclados

Isso requer o uso de ExpressionVisitorclasses para recursar a árvore de expressão e substituir os parâmetros das duas expressões pelo mesmo 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);
}

Quando ele estava prestes a se exibir para o irmãozinho, ele foi escrever outros códigos vistosos

Não é show, mas é show, vamos completar a lista, segue o código completo

Amigos que não querem mexer as mãos podem pesquisar diretamente no nuget DynamicExpression.Coree usá-lo diretamente

Para mais código fonte, veja meu 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();
}

Acho que você gosta

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