I am trying to perform a query against a list to give immediate results using an expression that is set elsewhere in the code, while a second thread goes off and uses it to get a full set of results from a database in a Linq query.
I know that the expression itself is OK as when I send it over the wire to the server side and apply it against an IQueryable then it will work. However, when applied on the client side the following error is produced:
System.InvalidOperationException: 'variable 'item' of type 'MissionControlSuite.Shared.Interface.Model.IBox' referenced from scope '', but it is not defined'
This code that performs the filtering:
public abstract class GridWithPaging<T> where T : class
{
List<T> _items
public Expression<Func<T, bool>> Filter { get; set; }
public ObservableCollection<IGridFilter<T>> Filters { get; set; }
// more code skipped for breveity
public void FiltersChanged(object sender, EventArgs e)
{
Expression<Func<T, bool>> combined = item => true;
foreach (var filter in Filters)
combined = Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(filter.Expression.Body, combined.Body),
combined.Parameters.Single());
Filter = combined;
NotifyPropertyChanged("Filter");
}
public void AddFilter(IGridFilter<T> filter)
{
Filters.Add(filter);
NotifyPropertyChanged("Filters");
}
private void DoFiltering(int start)
{
var newView = _items.Skip(start);
if(Filter != null)
newView = newView.AsQueryable().Where(Filter);
//some more code that acts on the filtered list
}
}
The expression is set elsewhere like this:
Expression<Func<IBox, bool>> expression = item => item.BoxId.Contains(v);
var filter = new GridFilter<IBox>()
{
Name = string.Format("{0} contains {1}", Property, Value),
Expression = expression
};
Grid.AddFilter(filter);
This answer will help you: Combining two expressions (Expression<Func<T, bool>>)
To summarize: The problem is that the parameters, although with the same name, are not the same instance of the ParameterExpression - which is included in the comment to that answer.
The solution is to use a Visitor (code copied over from linked post):
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
Also note: the ORM provider (or another IQueryable you use) probably works because it translates that to text directly, whereas executing (Invokeing) this LambdaExpression causes the error as it's more strict, in a way. Some IQueryables can also accept simple string parameters and parse that on their own, which should indicate that they have a wider range of acceptable inputs.
Related
I'm getting the following error
The parameter 'p' was not bound in the specified LINQ to Entities
query expression.
I understand the problem (same instance of ParameterExpression should be used with all the expressions in the tree) and have attempted to use solutions I've found online but with no luck.
This is my method
private void SeedEntity<TEntity>(DatabaseContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class
{
Expression<Func<TEntity, bool>> allExpresions = null;
var parameters = identifierExpressions.SelectMany(x => x.Parameters).GroupBy(x => x.Name).Select(p => p.First()).ToList();
foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
{
Func<TEntity, object> vv = identifierExpression.Compile();
object constant = vv(entity);
ConstantExpression constExp = Expression.Constant(constant, typeof(object));
BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, parameters);
if (allExpresions == null)
{
allExpresions = equalExpression2;
}
else
{
BinaryExpression bin = Expression.And(allExpresions.Body, equalExpression2.Body);
allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, parameters);
}
}
TEntity existingEntity = null;
if (allExpresions != null)
{
existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
}
if (existingEntity == null)
{
context.Set<TEntity>().Add(entity);
}
else
{
entity = existingEntity;
}
}
It generates an expression for the lookup of an entity based on a number of properties.
It works fine for a single expression, the error only occurs when passing in multiple.
Called like this:
SeedEntity(context, ref e, p=> p.Name);//Works
SeedEntity(context, ref e, p=> p.Name, p=> p.Age);//Fails
It generates something similar to me performing the following:
context.Set<TEntity>().FirstOrDefault(p=>p.Name == e.Name && p.Age == e.Age);
Replacing e.Name && e.Age with a ConstantExpression
You can see in the method above I grab all of the unique params and store them in parameters at the top, then use the same variable throughout.This is the start, but then I need to replace the instances of the parameter in each of the Expression<Func<TEntity, bool>> passed in as the params array, this is where I'm failing.
I've tried enumerate the expressions and use the .Update() method passing in the params
I also tried a solution using the ExpressionVisitor
public class ExpressionSubstitute : ExpressionVisitor
{
public readonly Expression from, to;
public ExpressionSubstitute(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
if (node == from) return to;
return base.Visit(node);
}
}
public static class ExpressionSubstituteExtentions
{
public static Expression<Func<TEntity, TReturnType>> RewireLambdaExpression<TEntity, TReturnType>(Expression<Func<TEntity, TReturnType>> expression, ParameterExpression newLambdaParameter)
{
var newExp = new ExpressionSubstitute(expression.Parameters.Single(), newLambdaParameter).Visit(expression);
return (Expression<Func<TEntity, TReturnType>>)newExp;
}
}
You're really close. I don't see the point of your parameters variable. Grouping them by name is a mistake. Why not just pass the parameters from the expression? Then visit if necessary. Your visitor code is fine.
private static void SeedEntity<TEntity>(DbContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions)
where TEntity : class
{
Expression<Func<TEntity, bool>> allExpresions = null;
foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
{
Func<TEntity, object> vv = identifierExpression.Compile();
object constant = vv(entity);
ConstantExpression constExp = Expression.Constant(constant, typeof(object));
BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, identifierExpression.Parameters);
if (allExpresions == null)
{
allExpresions = equalExpression2;
}
else
{
var visitor = new ExpressionSubstitute(allExpresions.Parameters[0], identifierExpression.Parameters[0]);
var modifiedAll = (Expression<Func<TEntity,bool>>)visitor.Visit(allExpresions);
BinaryExpression bin = Expression.And(modifiedAll.Body, equalExpression2.Body);
allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, identifierExpression.Parameters);
}
}
TEntity existingEntity = null;
if (allExpresions != null)
{
existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
}
if (existingEntity == null)
{
context.Set<TEntity>().Add(entity);
}
else
{
entity = existingEntity;
}
}
I have an Expression<Func<Entity, string>> that can be either a property or a nested property accessor
y => y.SearchColumn
or
y => y.SubItem.SubColumn
I am building up an expression tree dynamically and would like to get an InvokeExpression like this
x => x.SearchColumn.Contains("foo");
x => x.Sub.SearchColumn.Contains("foo");
I'm pretty sure I can unwrap the Expression body, then partially apply the x ParameterExpression to it but I can't figure out the magical incantations to make this happen
So I have something like
Expression<Func<Entity, string>> createContains(Expression<Func<Entity, string>> accessor) {
var stringContains = typeof(String).GetMethod("Contains", new [] { typeof(String) });
var pe = Expression.Parameter(typeof(T), "__x4326");
return Expression.Lambda<Func<Entity, bool>>(
Expression.Call(
curryExpression(accessor.Body, pe),
stringContains,
Expression.Constant("foo")
)
, pe
);
}
static Expression curryExpression(Expression from, ParameterExpression parameter) {
// this doesn't handle the sub-property scenario
return Expression.Property(parameter, ((MemberExpression) from).Member.Name);
//I thought this would work but it does not
//return Expression.Lambda<Func<Entity,string>>(from, parameter).Body;
}
Edit:
Here is the full thing I'm trying to do along with the error
You need to do two things - first one you can use the same accessor.Body, but it will reference to incorrect Parameter, as you created a new one. Second one you need to write custom ExpressionVisitor that will replace all usage of original y ParameterExpression to a new created, so result expression will be compiled fine.
If you don't need to create new ParameterExpression, then you can just use the same accessor.Body and resent original ParameterExpression to a new tree.
So here is my test working copy with a new ParameterExpression - https://dotnetfiddle.net/uuPVAl
And the code itself
public class Program
{
public static void Main(string[] args)
{
Expression<Func<Entity, string>> parent = (y) => y.SearchColumn;
Expression<Func<Entity, string>> sub = (y) => y.Sub.SearchColumn;
var result = Wrap(parent);
Console.WriteLine(result);
result.Compile();
result = Wrap(sub);
Console.WriteLine(result);
result.Compile();
result = Wrap<Entity>((y) => y.Sub.Sub.Sub.SearchColumn);
Console.WriteLine(result);
result.Compile();
}
private static Expression<Func<T, bool>> Wrap<T>(Expression<Func<T, string>> accessor)
{
var stringContains = typeof (String).GetMethod("Contains", new[] {typeof (String)});
var pe = Expression.Parameter(typeof (T), "__x4326");
var newBody = new ParameterReplacer(pe).Visit(accessor.Body);
var call = Expression.Call(
newBody,
stringContains,
Expression.Constant("foo")
);
return Expression.Lambda<Func<T, bool>>(call, pe);
}
}
public class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _target;
public ParameterReplacer(ParameterExpression target)
{
_target = target;
}
protected override Expression VisitParameter(ParameterExpression node)
{
// here we are replacing original to a new one
return _target;
}
}
public class Entity
{
public string SearchColumn { get; set; }
public Entity Sub { get; set; }
}
PS: this example will work only if you have only one ParameterExpression in original query, otherwise visitor should differentiate them
UPDATE
Here is my working answer with your full example in update - https://dotnetfiddle.net/MXP7wE
You just need to a fix a couple of things:
Return type of your method should be Expression<Func<T, bool>>.
The 1st parameter to Expression.Call() should simply be accessor.Body.
The ParameterExpression parameter to the Expression.Lambda<Func<T, bool>>() method call should simply be set from the accessor's parameter.
Method:
Expression<Func<T, bool>> CreateContains<T>(Expression<Func<T, string>> accessor)
{
var stringContains = typeof(String).GetMethod("Contains", new[] { typeof(String) });
return Expression.Lambda<Func<T, bool>>(
Expression.Call(
accessor.Body,
stringContains,
Expression.Constant("foo")
)
, accessor.Parameters[0]
);
}
I would like to have generic method for filtering Entity Framework IQueryable<TItem> by IEnumerable<TEnum>. Its signature should probably look like this:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TItem, IEnumerable<TEnum>, bool>> predicate)
{
return items.Where(??????);
}
and I would want to be able to call it for example like this:
IQueryable<Request> requests = service.GetAllRequests();
IEnumerable<RequestState> states = new RequestState[] {RequestState.Active, RequestState.Closed};
Expression<Func<Request, IEnumerable<RequestState>, bool>> predicate = (r, s) => s.Contains(r.State);
requests = ApplyFilter(requests, states, predicate);
But what should be inside method's body? How can I convert Expression<Func<TItem, IEnumerable<TEnum>, bool>> to Expression<Func<TItem, bool>> for use as parameter to "Where" method? Will it work with Entity Framework?
IMO, the predicate should be inside your ApplyFilter method (respect of concerns).
One possible way to code this would be :
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items,
IEnumerable<TEnum> enumValues, Expression<Func<TItem,TEnum>> enumField)
{
return items.Where(i => enumValues.Contains(enumField(i)));
}
with a call like this :
requests = ApplyFilter(requests, states, r => r.State);
When I was implementing Sharped's answer I found that it is actually possible to do exactly what I want. The point is to use custom class that inherits from ExpressionVisitor. This class will replace every occurrence of IEnumerable<TEnum> parameter within expression tree with its actual value. My final code:
public static IQueryable<TFilteredItem> ApplyFilter<TFilteredItem, TEnum>(IQueryable<TFilteredItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TFilteredItem, IEnumerable<TEnum>, bool>> predicate)
{
ParameterExpression itemParam = predicate.Parameters[0];
ParameterExpression enumsParam = predicate.Parameters[1];
var em = new ExpressionModifier<IEnumerable<TEnum>>(enumsParam.Name, enumValues);
Expression predicateBody = em.Modify(predicate.Body);
return items.Where(Expression.Lambda<Func<TFilteredItem, bool>>(predicateBody, new[] { itemParam }));
}
public class ExpressionModifier<T> : ExpressionVisitor
{
private readonly string parameterName;
private readonly T newValue;
public Expression Modify(Expression expression)
{
return Visit(expression);
}
public ExpressionModifier(string parameterName, T newValue)
{
this.parameterName = parameterName;
this.newValue = newValue;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == this.parameterName ? Expression.Constant(this.newValue, node.Type) : base.VisitParameter(node);
}
}
And I am using it like:
var requests = this.ServiceManager.Messaging.GetRequests();
var states = new[] {RequestState.Active};
requests = ApplyFilter(requests, states, (i, e) => e.Contains(i.State));
In MVC4 , I am providing Search box to the user to search any value in Table.
So I am implementing Generic Filter Condition at server side in C#
Need a help to combine multiple expressions to form single expression
Expression<Func<T, bool>>
For Example
Table Columns
MenuText, Role Name (Role.Name mapping), ActionName
Now If user entered in search box for ABC , which can be in any of the rows in shown columns, need to filter.
Model
public class Menu
{
public string MenuText {get;set;}
public Role Role {get;set;}
public string ActionName {get;set;}
}
public class Role
{
public string Name {get;set;}
}
So far I have implemented
/// <summary>
/// string[] properties property.Name (MenuText, ActionName), including deeper Mapping names such as (Role.Name)
/// </summary>
public static Expression<Func<T, bool>> FilterKey<T>(string filterText, params string[] properties)
{
ParameterExpression parameter = Expression.Parameter(typeof (T));
Expression[] propertyExpressions = properties.Select(
x => !string.IsNullOrEmpty(x) ? GetDeepPropertyExpression(parameter, x) : null).ToArray();
Expression<Func<T, bool>> predicate = PredicateBuilder.False<T>();
foreach (Expression expression in propertyExpressions)
{
var toLower = Expression.Call(expression, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
var like = Expression.Call(toLower, typeof(string).GetMethod("Contains"), Expression.Constant(filterText.ToLower()));
//TODO: Combine expressions to form single Expression<Func<T, bool>> expression
}
return predicate;
}
/// <summary>
/// To Get Deeper Properties such as Role.Name Expressions
/// </summary>
private static Expression GetDeepPropertyExpression(Expression initialInstance, string property)
{
Expression result = null;
foreach (string propertyName in property.Split('.'))
{
Expression instance = result ?? initialInstance;
result = Expression.Property(instance, propertyName);
}
return result;
}
I have created a few search IQueryable extension methods that you should be able to use
Full blog post is here:
http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable
GitHub project is here (has a couple of extra extensions for OR searches:
https://github.com/ninjanye/SearchExtensions
public static class QueryableExtensions
{
public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, string searchTerm)
{
if (String.IsNullOrEmpty(searchTerm))
{
return source;
}
// The below represents the following lamda:
// source.Where(x => x.[property] != null
// && x.[property].Contains(searchTerm))
//Create expression to represent x.[property] != null
var isNotNullExpression = Expression.NotEqual(stringProperty.Body, Expression.Constant(null));
//Create expression to represent x.[property].Contains(searchTerm)
var searchTermExpression = Expression.Constant(searchTerm);
var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);
//Join not null and contains expressions
var notNullAndContainsExpression = Expression.AndAlso(isNotNullExpression, checkContainsExpression);
var methodCallExpression = Expression.Call(typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(notNullAndContainsExpression, stringProperty.Parameters));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
}
This allows you to write something like:
string searchTerm = "test";
var results = context.Menu.Search(menu => menu.MenuText, searchTerm).ToList();
//OR for Role name
string searchTerm = "test";
var results = context.Menu.Search(menu => menu.Role.Name, searchTerm).ToList();
You might also find the following posts useful:
Search extension method that allows search accros multiple properties:
http://jnye.co/Posts/7/generic-iqueryable-or-search-on-multiple-properties-using-expression-trees
Search extension method that allows multiple or search terms on a property:
http://jnye.co/Posts/8/generic-iqueryable-or-search-for-multiple-search-terms-using-expression-trees
Thanks to NinjaNye , I have borrowed BuildOrExpression which resolved my problem
Here is the solution
public static Expression<Func<T, bool>> FilterKey<T>(string filterText, params string[] properties)
{
ParameterExpression parameter = Expression.Parameter(typeof (T));
Expression[] propertyExpressions = properties.Select(
x => !string.IsNullOrEmpty(x) ? GetDeepPropertyExpression(parameter, x) : null).ToArray();
Expression like= propertyExpressions.Select(expression => Expression.Call(expression, typeof (string).GetMethod("ToLower", Type.EmptyTypes))).Select(toLower => Expression.Call(toLower, typeof (string).GetMethod("Contains"), Expression.Constant(filterText.ToLower()))).Aggregate<MethodCallExpression, Expression>(null, (current, ex) => BuildOrExpression(current, ex));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
if (existingExpression == null)
{
return expressionToAdd;
}
//Build 'OR' expression for each property
return Expression.OrElse(existingExpression, expressionToAdd);
}
private static Expression GetDeepPropertyExpression(Expression initialInstance, string property)
{
Expression result = null;
foreach (string propertyName in property.Split('.'))
{
Expression instance = result ?? initialInstance;
result = Expression.Property(instance, propertyName);
}
return result;
}
i want to simplify code to add loadoptions and association filtering so i created this class.
class GraphQuery<T>
{
private IQueryable<T> query;
private DataLoadOptions load;
public GraphQuery(DataLoadOptions load, IQueryable<T> query)
{
this.load = load;
this.query = query;
}
public GraphQuery<T> Load(
Expression<Func<T, object>> expr,
Expression<Func<T, object>> filter)
{
load.LoadWith(expr);
load.AssociateWith(filter);
return this;
}
// more public methods ...
}
it can then be used like this:
var clients = Graph(db.Clients.Where(e => !e.Deleted))
.Load(e => e.ClientPersons,
e => e.ClientPersons.Where(j => !j.Person.Deleted));
however, i see a very simple repetition of e => e.ClientPersons. so i would like to reduce the above usage to :
var clients = Graph(db.Clients.Where(e => !e.Deleted))
.Load(e => e.ClientPersons.Where(j => !j.Person.Deleted));
so the Load function should look something like
public GraphQuery<T> Load(Expression<Func<T, object>> filter)
{
var expr = ... extract first part of the expression that represents the association property
load.LoadWith(expr);
load.AssociateWith(filter);
return this;
}
i have never worked with linq expressions except for using them in queries
i used the debugger to see what i could get from the expression and found it rather simple to extract what i needed. i guess its not pretty, but it gets the job done. if anybody has some recommendations to improve this - please post a comment.
private static LambdaExpression GetRootMemberExpression(LambdaExpression lambda1)
{
var expr = lambda1.Body;
while (!FindRoot(ref expr)) ;
if (!(expr is MemberExpression))
throw new Exception("MemberExpression required");
return Expression.Lambda(expr, (expr as MemberExpression).Expression as ParameterExpression);
}
private static bool FindRoot(ref Expression expr)
{
if (expr is MemberExpression)
return FindRoot(ref expr, (expr as MemberExpression).Expression);
if (expr.NodeType == ExpressionType.Call)
return FindRoot(ref expr, (expr as MethodCallExpression).Object);
throw new Exception("Unexpected Expression type encountered (" + expr.NodeType + ")");
}
private static bool FindRoot(ref Expression expr, Expression expr2)
{
if (expr2.NodeType == ExpressionType.Parameter)
return true;
expr = expr2;
return false;
}