I got a linq expression,
internal T Execute<T>(System.Linq.Expressions.Expression<Func<int, string, T>> expr)
{
var paramInt = ??;
var paramString = ??;
}
I call this method using this:
Expression<Func<int, string, Guid>> myExpression2 = (a,b) => Callmethod(a, b, 5);
Execute<Guid>(myExpression2);
How can i get the parameters from the expression in my execute method ?
-- To clarify --
I want to get the values from the parameters so that i do some calculations with them.
Well, you can get the parameters using the Parameters property.
var parameters = expr.Parameters;
However, each of those will be a ParameterExpression. That will give you the parameter name and the type, but it's not clear what you want to do with them.
Related
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.
My goal is to support sorting in an application and expose it via REST API that would accept the parameter as a string.
Current implementation is along the lines of this:
GetUsers (Expression<Func<User, int>> sortExpression) {
// Retrieve users ...
return users.orderBy(sortExpression);
}
Usage example:
var users = GetUsers(u => u.Id);
the Expression<Func<User, int>> sortExpression is widely used in our repository and changing it would be difficult.
What I'd like to do is to be able to swap the u => u.Id with something that is generated during run-time.
Something that I have so far is:
// sortBy is retrieved from the calling method.
var type = typeof(User).GetProperties().FirstOrDefault(x => x.Name == sortBy).GetType();
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
var users = GetUsers(expressionBody)
I can see at run-time that this does create an expression that fits my needs, but the error is Argument 5: cannot convert from 'LambdaExpression' to 'Expression<System.Func<User, int>>' even though the body of the expression is supposed to be set by typeof(Func<User, int>)
I've figured out what I've been doing wrong.
First: Create the expression body using generic method
// Generic Method, return type Expression<Func<User, int>>
Expression.Lambda<Func<User, int>>(sortExpression, parameter);
Instead of passing the typeof(Func<User, int>) parameter.
// Non-generic. Return type LambdaExpression
Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
Second:
I wasn't binding the parameter properly, which made it so that the expression was accessing property of a discarded parameter that wasn't provided to the expression.
// I'm creating an expression to access the property of a newly created parameter.
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda<Func<User, int>>(sortExpression, parameter);
//Above causes an unbinded variable exception since there are two parameters, one of which is not passed/bound.
//Should be:
var parameter = Expression.Parameter(typeof(User));
var sortExpression = Expression.Property(parameter, sortBy);
I have declared multiple of these variables, but how can I then put them into a generic list?
Expression<Func<poco, string>> fieldToUpdate1 = x => x.Name;
Expression<Func<poco, bool>> fieldToUpdate2 = x => x.Id;
Currently I can only specify one type for the generic list.
So I can either get a List<string> or List<bool>. But not both. I want to be able to have a generic list that accepts both so I can pass that list as a parameter.
Use case:
The use case I am trying to do is create a generic wrapper for the Mongo method updateOne. With the below signature. I want to create a generic wrapper that will accept two parameters. I can use these parameters to call the actual mongo implementation. Something like this:
GenericWrapper(Expression<Func<TDocument, bool>> filter, List<(Expression<Func<TDocument, TField>> expression, TField actual value)>)
The problem is that TField can only be one type. So I can only do this:
Expression<Func<Student, string>> fieldToUpdate1 = x => x.name;
Expression<Func<Student, int>> fieldToUpdate2 = x => x.testScore;
var expressions = new List<(Expression<Func<Student, int>> expression, int value)>();
var item1 = (expression: fieldToUpdate2, value: 4);
var item2 = (expression: fieldToUpdate1, value: "test");
expressions.Add(item1);
//I can't add item2 since its of a different type. I can only pass a list of the same type. And my generic wrapper function will only accept a list of one type
http://api.mongodb.com/csharp/current/html/M_MongoDB_Driver_IMongoCollectionExtensions_UpdateOne__1.htm
public static UpdateResult UpdateOne<TDocument>(
this IMongoCollection<TDocument> collection,
Expression<Func<TDocument, bool>> filter,
UpdateDefinition<TDocument> update,
UpdateOptions options = null,
CancellationToken cancellationToken = null
)
Any ideas on how to make this generic wrapper?
Since Expression<T> inherits from Expression you can put it into a List<Expression>.
List<Expression> expressions = new List<Expression>();
expressions.Add(fieldToUpdate1);
expressions.Add(fieldToUpdate2);
You can use object as the return value:
Expression<Func<poco, object>> fieldToUpdate1 = x => x.Name;
Expression<Func<poco, object>> fieldToUpdate2 = x => x.Id;
List<Expression<Func<poco, object>>> testList = new List<Expression<Func<poco, object>>>();
testList.Add(fieldToUpdate1);
testList.Add(fieldToUpdate2);
Anyway, the general design seems a bit strange, since at the end, you have to cast at least the results.
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))
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));