es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Conversión de Expression<>> a string

Necesito convertir Expression<Func<T, bool>> a string. ¿Cómo puedo hacerlo?
Este es el predicado que estoy intentando convertir:

var prezziList = ExecQuery<Listino>(i => i.CodArt == articolo.CodArt);

Estoy utilizando el método sugerido aquí: https://stackoverflow.com/questions/4793981/converting-expressiont-bool-to-string/4794140, y este es mi método:

public List<TEntity> ExecQuery<TEntity>(Expression<Func<T, bool>> predicate)
{
    string expBody = predicate.Body.ToString();

    var paramName = predicate.Parameters[0].Name;
    var paramTypeName = predicate.Parameters[0].Type.Name;
    expBody = expBody.Replace(paramName + ".", paramTypeName + ".")
                 .Replace("AndAlso", "&&")
                 .Replace("==", "=");

    SQLiteCommand sQLiteCommand = new(App.CNManager.Connection);
    sQLiteCommand.CommandText = $"SELECT * FROM myTable WHERE {expBody}";

    return sQLiteCommand.ExecuteQuery<TEntity>();
}

Sin embargo, devuelve la siguiente cadena, que obviamente no está en el formato correcto:

"Listino.CodArt = value(Vendo.ViewModels.DettaglioArticoliViewModel+<>c<em>_DisplayClass184</em>0).articolo.CodArt"

¿Dónde estoy cometiendo un error?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con la implementación actual es que el cuerpo de la expresión representa la expresión lambda completa, incluyendo los nombres y tipos de parámetros que no son válidos en la sintaxis SQL. Para convertir la expresión en una cadena SQL válida, necesitamos extraer solo las condiciones y valores involucrados en la expresión.

    Para lograr esto, podemos usar un visitante de expresión que inspeccione el árbol de expresiones y construya una cadena SQL basada en los nodos visitados. Aquí hay una implementación de ejemplo:

    public static string ConvertExpressionToString<tentity>(Expression<><tentity, bool="">> expression)
    {
        var visitor = new SqlExpressionVisitor(typeof(TEntity));
        visitor.Visit(expression);
    
        return visitor.ToString();
    }
    
    class SqlExpressionVisitor : ExpressionVisitor
    {
        private readonly Type entityType;
        private readonly StringBuilder stringBuilder;
    
        public SqlExpressionVisitor(Type entityType)
        {
            this.entityType = entityType;
            this.stringBuilder = new StringBuilder();
        }
    
        protected override Expression VisitBinary(BinaryExpression node)
        {
            stringBuilder.Append("(");
            Visit(node.Left);
    
            switch (node.NodeType)
            {
                case ExpressionType.Equal:
                    stringBuilder.Append(" = ");
                    break;
                case ExpressionType.NotEqual:
                    stringBuilder.Append(" <> ");
                    break;
                case ExpressionType.GreaterThan:
                    stringBuilder.Append(" > ");
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    stringBuilder.Append(" >= ");
                    break;
                case ExpressionType.LessThan:
                    stringBuilder.Append(" < ");
                    break;
                case ExpressionType.LessThanOrEqual:
                    stringBuilder.Append(" <= ");
                    break;
                case ExpressionType.AndAlso:
                case ExpressionType.And:
                    stringBuilder.Append(" AND ");
                    break;
                case ExpressionType.OrElse:
                case ExpressionType.Or:
                    stringBuilder.Append(" OR ");
                    break;
                default:
                    throw new NotSupportedException($"'{node.NodeType}' no es compatible");
            }
    
            Visit(node.Right);
            stringBuilder.Append(")");
    
            return node;
        }
    
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType != ExpressionType.Not)
            {
                throw new NotSupportedException($"'{node.NodeType}' no es compatible");
            }
    
            stringBuilder.Append(" NOT ");
            Visit(node.Operand);
    
            return node;
        }
    
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != null && node.Expression.Type == entityType)
            {
                stringBuilder.Append(node.Member.Name);
            }
            else if (node.Expression == null)
            {
                var constant = (ConstantExpression)node.Expression;
                var value = constant.Value;
                stringBuilder.Append(GetSqlValue(value));
            }
            else
            {
                throw new NotSupportedException($"'{node}' no es compatible");
            }
    
            return node;
        }
    
        private static string GetSqlValue(object value)
        {
            switch (value)
            {
                case null:
                    return "NULL";
                case string _:
                    return $"\'{value}\'";
                case bool _:
                    return Convert.ToInt32(value).ToString();
                case char _:
                    return $"'{value}'";
                default:
                    return value.ToString();
            }
        }
    
        public override string ToString()
        {
            return stringBuilder.ToString();
        }
    }
    

    Luego podemos modificar el método ExecQuery para usar este método auxiliar y convertir la expresión en una cadena SQL:

    public List<tentity> ExecQuery<tentity>(Expression<><tentity, bool="">> predicate)
    {
        var sql = ConvertExpressionToString(predicate);
        var command = new SQLiteCommand(sql, App.CNManager.Connection);
        return command.ExecuteQuery<tentity>();
    }
    

    Con estos cambios, ahora deberíamos poder convertir la expresión en una cadena SQL válida y usarla para consultar la base de datos.</tentity,></tentity,>

Comments are closed.