Expression Func with two input parameters for generic method - c#

I want to integrate this expression
Expression<Func<Customer, string, bool>> paramCompareFunc = (cust, name) => cust.Company == name;
For this
private bool IsUnique<Entity>(DbSet<Entity> set,
string param,
Expression<Func<Entity, string, bool>> paramCompareFunc)
where Entity : class
{
var query = set.Where(paramCompareFunc); // how I can pass param to expression?
// var query = set.Where(paramCompareFunc(param)); // error here
...
How I can pass the second parameter to the expression?
I want to define different compare expressions for different entities (they don't have any same name field) and to have a possibility to pass this expression into my generic function.

The "easy" way is by changing your api to use a factory method to build the Expression you actually need;
Expression<Func<Customer, bool>> GetCompareFunc(string name) => (cust) => cust.Company == name;
While you could use ReplacingExpressionVisitor to swap the name parameter with a constant, that would have a negative impact on performance.

Related

Find Predicate parameters

Is there a way to find parameters which is passed in predicate variable. Lets say, I have this method;
List<User> GetUsers(Predicate<UserModel> userPredicate)
{
// how to find what values are passed in userPredicate
}
Function Call:
GetUsers(_ => _.Name == "abc");
How can I find that in predicate, Name property has been set to "abc" within GetUsers function?
public static void GetUsers(Expression<Func<UserModel, bool>> predicate)
{
var expr = predicate.Body as BinaryExpression;
var value = expr.Right as ConstantExpression;
// this will be the value of that predicate Func
var yourvalue = value.Value;
}
This is one way to do it, and you have to be careful about casting those expressions because it might not work if you change the body of expression.
FYI, I wouldn't recommend this solution at all to achieve what you need to do, there are better ways to design it but it gives you are looking for.

How to build a simple property selector expression in ef6

How can I create a property selector for entity framework like this?
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList();
}
I want the calling code to be able to be clean and simple like this:
var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query);
I get a "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." error. I cannot work out how to get the expression built.
UPDATE:
Based on the answer by MBoros here is the code I ended up with. It works great.
The key to expression trees is to understand expression trees are all about breaking up what you normally write in code (like "e => e.Username.IndexOf(query)") into a series of objects: "e" gets its own object, "Username" its own object, "IndexOf()" its own object, the "query" constant its own object, and so on. The second key is to know that you can use a series of static methods on the Expression class to create various kinds of these objects, as shown below.
PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member;
ParameterExpression parameter = Expression.Parameter(typeof(T), "e");
MemberExpression accessor = Expression.Property(parameter, pinfo);
ConstantExpression queryString = Expression.Constant(query, typeof(string));
ConstantExpression minusOne = Expression.Constant(-1, typeof(int));
MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this
Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString);
Expression expression = Expression.GreaterThan(indexOf, minusOne);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter);
//return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want.
var results = queryable.Where(predicate).ToList();
return results;
Now I have a real problem, but I will ask it in a separate question. My real query looks like this:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e } ).ToList();
}
So I need to build an expression that returns an Anonymous Type!! Or even if I create a class to help, I need to write an expression that returns a new object. But I will include this in a separate question.
You cannot invoke CLR delegates so simply in sql. But you can pass in the property selector as an Expression tree., so your signature would be:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)
Calling would look the same. But now that you have an expression in your hand, you can have a look at this answer:
Pass expression parameter as argument to another expression
It gives you the tools to simply put an expression tree inside another one. In your case it would look like:
Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();
Once you are there, you still have the .ToLower().Contains() calls (use .Contains instead of the .IndexOf()> 1). This is actually tricky. Normally the db uses its default collation, so if it set to CI (case insensitive), then it will do the compare that way. If you don't have any constraints, and can adjust the db collation, I would go for that. In this case you can omit the .ToLower() call.
Otherwise check out this anser: https://stackoverflow.com/a/2433217/280562

Create an expression tree that calls an expression tree

I have a property on a class
Expression<Func<Product, int>> predicate;
that will be assigned to different expressions throughout the application.
In a method called GetProducts I would like to retrieve Products from the DB using Entity Framework, using this predicate. I will then have a variable called myInt that I would like to use as the int parameter, that will then be assigned a value.
So I tried
dbContext.Products.Where(p => predicate(p, myInt))
but I got Non-invocable member 'predicate' cannot be used like a method. error.
It looks like I need to do some expression tree manipulation, to create a new expression tree, with myInt baked in it. How can I do this?
Thank you for your help.
You defined predicate as a Type but you are using it as a method.
You can define a predicate in the following way:
private bool predicate(Product product, int myInt)
{
//put your logic here
return true;
}
You can also use lambda expressions:
product => product.SomeValue > 5
Edit:
Expression<Func<Product, int>> predicate = (product,val) => product.SomeValue > val;
var filtered = dbContext.Products.Where(predicate);
Avoid using types when naming a parameters (i.e don't name an integer MyInt)
OK, got it.
ParameterExpression prm = Expression.Parameter(typeof(Product));
InvocationExpression inv = Expression.Invoke(predicate, prm, Expression.Constant(myInt));
var lambda = (Expression<Func<Product,bool>>) Expression.Lambda(inv, prm);
dbContext.Products.Where(lambda);
The key is Expression.Invoke that can invoke the predicate, using supplied parameters.
EDIT - After trying it, this only works with linq2sql - with Entity Framework it can't translate InvocationExpression to SQL. Instead, I used LinqKit, as follows:
dbContext.Products.AsExpandable().Where(p => predicate.Invoke(p, myInt))

Using a member access lambda expression to parametrise a LINQ to SQL predicate

I have a query that needs to be reused all over the place and I need to vary which property/column gets used for a join.
What I'd like to be able to do is something like:
query = RestrictByProp(query, x=>x.ID);
An extremely simplified RestrictByProp() could be*:
private static IQueryable<Role> RestrictByProp(IQueryable<Role> query,
Func<Role, int> selector)
{
return query.Where(x => selector(x) == 1);
}
The problem is that even this simple implementation causes a runtime exception:
Method 'System.Object DynamicInvoke(System.Object[])' has no
supported translation to SQL.
**(Here I'm just adding a simple 'where' clause - in my real code I'd be using the lambda to pick which property to use for a join).*
I find this strange because if the member access lambda is done inline it is fine:
private static IQueryable<Role> RestrictByID(IQueryable<Role> query)
{
return query.Where(x=> x.ID == 1);
}
LINQ to SQL is also happy if you pass in an Expression<Func<Role, bool>> (i.e. when the parameter is x=>x.ID == 1) but that defeats the object because I need the value of the right-hand operand to be determined within the query.
Is there a way to somehow munge the lambda expression in RestrictByProp() so that LINQ to SQL knows how to generate the SQL?
First, you need to change your method signature:
private static IQueryable<Role> RestrictByProp(IQueryable<Role> query,
Expression<Func<Role, int>> selector)
That will mean your lambda expression is converted into an expression tree instead of a delegate.
You'll then need to build an Expression<Func<Role, bool>> from the existing expression tree.
It will look something like this:
LambdaExpression lambda = (LambdaExpression) selector;
var predicate = Expression.Equal(selector, Expression.Constant(1));
var lambdaPredicate = Expression.Lambda<Func<Role, bool>>(predicate,
lambda.Parameters);
return query.Where(lambdaPredicate);

How to Transform a LINQ Expression when you do not have one of the parameters when you define it

I'm trying to build more generic query functionality into my application. What I'd like to do is define objects which given an predicate expression can apply that to an iqueryable with a value that will be passed in later.
I believe the code below should demonstrate what I'm trying to do well enough to understand the problem. Please let me know if you'd like more details!
Thanks!
//in practice the value of this would be set in object constructor likely
private Expression<Func<Contact, string, bool>> FilterDefinition = (c, val) => c.CompanyName.Contains(val);
//this needs to filter the contacts using the FilterDefinition and the filterValue. Filterval needs to become the string parameter
private IQueryable<Contact> ApplyFilter(IQueryable<Contact> contacts, string filterValue)
{
//this method is what I do know know how to contruct.
// I need to take the FilterDefinition expression and create a new expression that would be the result if 'filtervalue' had been passed into it when it was created.
//ie the result would be (if 'mycompany' was the value of filterValue) an expression of
// c => c.CompanyName.Contains("mycompany")
Expression<Func<Contact, bool>> usableFilter = InjectParametersIntoCriteria(FilterDefinition, "SomeCompanyName");
//which I could use the results of to filter my full results.
return contacts.Where(usableFilter);
}
Are you looking for something like this?
private Func<string, Expression<Func<Contact, bool>>> FilterDefinition =
val => c => c.CompanyName.Contains(val);
private IQueryable<Contact> ApplyFilter(
IQueryable<Contact> contacts, string filterValue)
{
Expression<Func<Contact, bool>> usableFilter = FilterDefinition(filterValue);
return contacts.Where(usableFilter);
}
See: Currying
Place the following code in your ApplyFilter body:
var f = FilterDefinition.Compile();
return contacts.Where(x => f(x, filterValue));

Categories

Resources