Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expression.Like in C#

eg: x=> x.Name = "g"

I have code block like this

public Expression<Func<TEntity, bool>> SearchExpression()
{
    var c = new ConstantExpression[_paramList.Count];
    var b = new BinaryExpression[_paramList.Count];
    BinaryExpression comparisonExpression = null;

    var entity = Expression.Parameter(typeof(TEntity));

    for (int i = 0; i < _paramList.Count; i++)
    {
        var value = Convert.ChangeType(_paramList[i].Item2 /*"g"*/, _paramList[i].Item3 /*System.String*/);
        c[i] = Expression.Constant(value); //"g"

        // PROBLEM IS HERE
        b[i] = Expression.Equal(Expression.Property(entity, _paramList[i].Item1 /*Name*/, c[i]);
        // PROBLEM IS HERE



    }
    _paramList.Clear();
    comparisonExpression = b.Aggregate(Expression.And);
    return Expression.Lambda<Func<TEntity, bool>>(comparisonExpression, entity);
}

works like charm but I need Expression.Like (Like "g" not Equal "g")

Expression.Like(Expression.Property(entity, _paramList[i].Item1), c[i])

but C# expression tree does not support Like method

UPDATE :

I wrote something like this :

Expression.Call(Expression.Property(entity, _paramList[i].Item1),
                typeof(String).GetMethod("Contains"), new Expression[] { c[i] });  

but I need BinaryExpression not MethodCallExpression

like image 912
Hamed F Avatar asked Jun 25 '14 09:06

Hamed F


2 Answers

You can make your code work by adding an equals expression over the method call, like so:

    b[i] = Expression.Equal(
        Expression.Call(Expression.Property(entity, _paramList[i].Item1),
        typeof (String).GetMethod("Contains"), 
          new Expression[] {c[i]}), Expression.Constant(true));

In pseudo code this reads as:

b[i] = entity => entity.someProperty.Contains(c[i]) == true;

Which will return a binary expression for you.

like image 68
Roger Johansson Avatar answered Sep 20 '22 12:09

Roger Johansson


This answer does not consider your array and the 'and' aggregation, but this should be considered as a separate issue.

Consider this class:

class MyEntity { string Name { get; set; } }

We want to query:

select ... from MyEntity where Name like '%query%';

The following method is a general implementation of the above query pattern:

static Expression<Func<TEntity, bool>> Like<TEntity>(string propertyName, string queryText)
{
    var parameter = Expression.Parameter(typeof (TEntity), "entity");
    var getter = Expression.Property(parameter, propertyName);
    //ToString is not supported in Linq-To-Entities, throw an exception if the property is not a string.
    if (getter.Type != typeof (string))
        throw new ArgumentException("Property must be a string");
    //string.Contains with string parameter.
    var stringContainsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)});
    var containsCall = Expression.Call(getter, stringContainsMethod,
        Expression.Constant(queryText, typeof (string)));

    return Expression.Lambda<Func<TEntity, bool>>(containsCall, parameter);
}

If you want to have a pattern of query% or %query you can use string.StartsWith and string.EndsWith instead of Contains.

Also, you can share the parameter across multiple calls if you adjust the signature.

The current implementation throws an exception if the data type of the property is not a string. Look at this answer https://stackoverflow.com/a/3292773/668272 for converting numbers to strings.

like image 40
Bas Avatar answered Sep 19 '22 12:09

Bas