Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can i get a string from linq expression?

I have this method and parameter.

void SomeMethod(Expression<Func<Products, bool>> where)

I call this method like this;

int i = 9;
SomeMethod(x=>x.Id==i)

And I want it to produce this string;

"x=>x.Id==9"

If I just print out the above expression as it is it will give me this string:

"x => (x.Id == value(isTakibi.WepApp.Controllers.HomeController+<>c__DisplayClass4_0).i)"

but I need "x.Id == 9". I need to evaluate the value of the variable i so that the result will be "x.id==9".

like image 614
Recep Gündoğdu Avatar asked Dec 07 '18 20:12

Recep Gündoğdu


People also ask

Can you use Linq on a string?

LINQ can be used to query and transform strings and collections of strings. It can be especially useful with semi-structured data in text files. LINQ queries can be combined with traditional string functions and regular expressions. For example, you can use the String.

What is LINQ expression?

Language-Integrated Query (LINQ) is the name for a set of technologies based on the integration of query capabilities directly into the C# language. Traditionally, queries against data are expressed as simple strings without type checking at compile time or IntelliSense support.

What is Dynamic Linq?

The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.


2 Answers

The way to simplify an expression in general is to compile it and execute the compiled delegate. Now you can't do that for any expression that still has any parameter expressions in it, because you don't know what the parameter's value will be (yet). This means we have two fundamental steps, first, determine which of the sub-expressions in our tree actually contain a parameter somewhere within that sub-tree, then evaluate all of the ones that don't.

So the first step is to determine which expressions contain a parameter within them. To do that we create an expression visitor that has a field indicating whether it's currently inside of a sub-tree with a parameter which then recursively checks its children, then checks itself, and then combines the results, adding all parameterless expressions into a collection while along the way.

private class ParameterlessExpressionSearcher : ExpressionVisitor
{
    public HashSet<Expression> ParameterlessExpressions { get; } = new HashSet<Expression>();
    private bool containsParameter = false;

    public override Expression Visit(Expression node)
    {
        bool originalContainsParameter = containsParameter;
        containsParameter = false;
        base.Visit(node);
        if (!containsParameter)
        {
            if (node?.NodeType == ExpressionType.Parameter)
                containsParameter = true;
            else
                ParameterlessExpressions.Add(node);
        }
        containsParameter |= originalContainsParameter;

        return node;
    }
}

Next to evaluate the sub-expressions that have no parameter we need another visitor. This one just has to check if the expression is in the set of expressions we found with the previous visitor, and if so, compiles that expression into a parameterless delegate and executes it, otherwise it'll check its children to see if any of them can be replaced.

private class ParameterlessExpressionEvaluator : ExpressionVisitor
{
    private HashSet<Expression> parameterlessExpressions;
    public ParameterlessExpressionEvaluator(HashSet<Expression> parameterlessExpressions)
    {
        this.parameterlessExpressions = parameterlessExpressions;
    }
    public override Expression Visit(Expression node)
    {
        if (parameterlessExpressions.Contains(node))
            return Evaluate(node);
        else
            return base.Visit(node);
    }

    private Expression Evaluate(Expression node)
    {
        if (node.NodeType == ExpressionType.Constant)
        {
            return node;
        }
        object value = Expression.Lambda(node).Compile().DynamicInvoke();
        return Expression.Constant(value, node.Type);
    }
}

Now we just need a simple method to first execute the first searcher, then the second, and return the results, and provide an overload that casts the result to a generic expression:

public static class ExpressionExtensions
{
    public static Expression Simplify(this Expression expression)
    {
        var searcher = new ParameterlessExpressionSearcher();
        searcher.Visit(expression);
        return new ParameterlessExpressionEvaluator(searcher.ParameterlessExpressions).Visit(expression);
    }

    public static Expression<T> Simplify<T>(this Expression<T> expression)
    {
        return (Expression<T>)Simplify((Expression)expression);
    }

    //all previously shown code goes here

}

Now you can write:

Expression<Func<Products, bool>> e = x => x.Id == i;
e = e.Simplify();
Console.WriteLine(e);

And it'll print:

"x => (x.Id == 9)"

Alternatively, if you just want to evaluate one particular expression, and you're willing to change how you write the expression in the first place to accommodate, this answer shows how you can write a method to indicate what sub-expressions should be evaluated, and to evaluate just those expressions. That would be useful if you want to evaluate some things and not others.

like image 111
Servy Avatar answered Sep 19 '22 16:09

Servy


.ToString() works for me:

void SomeMethod(Expression<Func<Product, bool>> where)
{
    Console.WriteLine(where.ToString());
}

When calling with

SomeMethod(x=>x.Id==9);

Outputs:

x => (x.Id == 9)

like image 20
Alex Avatar answered Sep 18 '22 16:09

Alex