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

6
  • how about adding an interface to your entity? e.g. class MyEntity:INameable with property string Name and then you can work with Func<INameable, bool> dynamically Commented Aug 29, 2016 at 11:56
  • What does propertyAccess variable contain? String property accessor? So sel is actually Expression<Func<TEntity, string>> rather than Expression<Func<TEntity, bool>> as in the example? Commented Aug 29, 2016 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 Commented Aug 29, 2016 at 12:01
  • the propperyAccess contains a MemberExpression in this example {x.Name} Commented Aug 29, 2016 at 12:05
  • 1
    I'm pretty sure you are using the overload IEnumerable<T>.Where(Func<T, bool>) overload which accepts an Func<T, bool> instead of IQueryable<T>.Where(Expression<Func<T, bool>>) which accepts an expression. Your problem is not that 'it works with IQueryable, but not with EF`. Commented Aug 29, 2016 at 12:12

2 Answers 2

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);
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.