4

I am using entity framework, and I need to create a dynamic expressions like:

var sel = Expression.Lambda<Func<TEntity, bool>>(propertyAccess, parameter);
var compiledSel = sel.Compile();
// sel = x.Name
// filter.Value = "John"
Repository.GetData.Where(item => compiledSel(item) != null && compiledSel(item).ToLower().StartsWith(filter.Value.ToString().ToLower()))

The above works with IQueriable, but I need it to work with entity framework.

That means I need to parse

item => compiledSel(item) != null && compiledSel(item).ToLower().StartsWith(filter.Value.ToString().ToLower())

to e.g.

x => x.Name != null && x.Name.StartsWith("John")

The reason I am doing this is because I have multiple entities I want to be able to filter dynamically.

Any suggestions?

Edit:

The query itself against EF is run here:

private IList<TEntity> GetCollection(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, object>>[] includes)
{
    return DbSet 
     .Where(where)
     .ApplyIncludes(includes)
     .ToList();
}

When I run the query now the data where clause is Param_0 => (((Invoke(value(.... and I get The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. error

h3li0s
  • 603
  • 8
  • 23
  • how about adding an interface to your entity? e.g. `class MyEntity:INameable` with property `string Name` and then you can work with `Func` dynamically – Matthias Burger Aug 29 '16 at 11:56
  • What does `propertyAccess` variable contain? String property accessor? So `sel` is actually `Expression>` rather than `Expression>` as in the example? – Ivan Stoev Aug 29 '16 at 11:59
  • The problem with adding INameble, is that I have a lot of entities that are different, so "x.Name" is just an example, it could be a quite a big number of other things – h3li0s Aug 29 '16 at 12:01
  • the propperyAccess contains a MemberExpression in this example {x.Name} – h3li0s Aug 29 '16 at 12:05
  • Can you show us where you're defining `propertyAccess` and `parameter`? And have you taken a look at this question: http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet/233505#233505 ? – Max Aug 29 '16 at 12:11
  • 1
    I'm pretty sure you are using the overload `IEnumerable.Where(Func)` overload which accepts an `Func` instead of `IQueryable.Where(Expression>)` which accepts an expression. Your problem is not that 'it works with IQueryable, but not with EF`. – Maarten Aug 29 '16 at 12:12

2 Answers2

4

First off, if the propertyAccess is accessor to a string property, the following

var sel = Expression.Lambda<Func<TEntity, bool>>(propertyAccess, parameter);

should be

var sel = Expression.Lambda<Func<TEntity, string>>(propertyAccess, parameter);

Second, the Compile does not work inside the EF expressions. You can build the whole predicate expression manually using the methods of the Expression class, but that's relatively hard. What I could suggest you is to use a "prototype" expression and simple parameter replacer like this:

var selector = Expression.Lambda<Func<TEntity, string>>(propertyAccess, parameter);

var value = filter.Value.ToString().ToLower();

Expression<Func<string, bool>> prototype = 
    item => item != null && item.ToLower().StartsWith(value);

var predicate = Expression.Lambda<Func<T, bool>>(
    prototype.Body.ReplaceParameter(prototype.Parameters[0], selector.Body), 
    selector.Parameters[0]);

Here is the code of the used helper method:

public static class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}
Ivan Stoev
  • 159,890
  • 9
  • 211
  • 258
1

Dynamic Linq might be an option for you:

Repository.People.Where("Name != null && Name.StartsWith(\"John\")")

https://www.nuget.org/packages/System.Linq.Dynamic

dchanko
  • 351
  • 5
  • 7