I have below query :
var result = _workOrders.GroupBy(row => row.Asset, (k, g) => new AverageBetweenTwoFailureViewModel
{
// fields
}).ToList();
and know I need to set field dynamicly in row => row.Asset dynamicly .
I searched on the Net and I found below codes :
var arg = Expression.Parameter(typeof(Item), "item");
var body = Expression.Property(arg, "D");
var lambda = Expression.Lambda<Func<Item, DateTime>>(body, arg);
var keySelector = lambda.Compile();
but that override that I used in Groupby it's different.
Related
The following code works for me if I need to filter a generic query for a single parameter, like Status=1.
public static IQueryable<T> FilterBy<T>(this IQueryable<T> query)
{
var propertyName = "Status";
var param_1 = "1";
//var param_2 = 2;
//var param_3 = "DE";
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
var converter = TypeDescriptor.GetConverter(propertyExp.Type);
var result = converter.ConvertFrom(param_1);
var value = Expression.Constant(result);
var equalBinaryExp = Expression.Equal(propertyExp, value);
query = query.Where(Expression.Lambda<Func<T, bool>>(equalBinaryExp, parameterExp));
return query;
}
This is like filtering a list with a value.
var list = new List<Employee> {
new Employee { Id = 1, Name = "Phil", Status=1, Country="DE" } ,
new Employee { Id = 2, Name = "Kristina", Status=2, Country="DE" },
new Employee { Id = 3, Name = "Mia", Status=1, Country="US" }
};
list = list.Where(x => (x.Status == 1)).ToList();
But how should I modify the method FilterBy, so that it will work for filtering multiple values? Like (Status=1 or Status=2) and Country="DE",
list = list.Where(x => (x.Status == 1 || x.Status == 2) && x.Country == "DE").ToList();
Thanks for your time.
The trick when writing lambdas by hand is to write them how you would in regular C#, and decompile them, perhaps using sharplab like this. If you look on the right, you can see that it is doing something like:
Expression.AndAlso(
Expression.OrElse(
Expression.Equal(idPropExp, Expression.Constant(1)),
Expression.Equal(idPropExp, Expression.Constant(2))
),
Expression.Equal(countryPropExp, Expression.Constant("DE"))
)
I want to populate the itemsource of a ComboBox with items from my List, depending on which property from T is selected.
The statement should be like:
foreach property which is a string,
select the values of the property, make distinct.
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda(callExpr) ;
// From here on it goes wrong!!!
var comleteExpr = lambdaExpr as Expression<Func<T, bool>>;
var compiledExpr = comleteExpr.Compile();
var res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}
The problem seems to be the casting from the lambda expression to prepare for compilation.
Thank you for your help.
First of all you need to provide paramExp to lambda. Secondly there is generic version of Lamda method which is just easier to use. Finally, you don't need to compile expression when you use IQueryable. You created queryableData variable and didn't use it.
Here is code:
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda<Func<T, bool>>(callExpr, paramExp);
var res = queryableData.Select(lambdaExpr).Distinct().ToList();
// add to results ...
}
return null;
}
I think you should check if the casting result is not null :
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
IQueryable<T> queryableData = data.AsQueryable();
ParameterExpression paramExp = Expression.Parameter(typeof(T), "left");
foreach (PropertyInfo pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
MemberExpression callExpr = Expression.MakeMemberAccess(paramExp, pi);
LambdaExpression lambdaExpr = Expression.Lambda(callExpr);
// From here on it goes wrong!!!
if (!(lambdaExpr is Expression<Func<T, bool>> comleteExpr)) continue;
Func<T, bool> compiledExpr = comleteExpr.Compile();
List<bool> res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}
consider the following pseudp code
Expression<Func<E,Datetime?>> expr =
e => (e.d1 ?? e.Rel.d1) ?? e.d2;
var q = myContext.E_DbSet.Select(x => new someDeclaredType {
Id = x.Id,
V = expr
});
=> CS0029 Cannot implicitly convert type 'System.Linq.Expressions.Expression>' to 'System.DateTime?'
if I try with compile
Expression<Func<E,Datetime?>> expr =
(e => (e.d1 ?? e.Rel.d1) ?? e.d2).Compile();
var q = myContext.E_DbSet.Select(x => new someDeclaredType {
Id = x.Id,
V = expr(x)
});
=> NotSupportedException: node type 'Invoke' of LINQ expression is not suported in LINQ to Entities.
I read Invoke an Expression in a Select statement - LINQ to Entity Framework. But this is not my exact case. I need to include an existing expression into my select clause. This expression comes as the result of a function call. So I must use expr in my select clause.
How can it be done ?
Maybe something like this:
Expression<Func<E,someDeclaredType>> expr =
e => new someDeclaredType()
{
Id = e.Id,
V = (e.d1 ?? e.Rel.d1) ?? e.d2
};
var q = myContext.E_DbSet.Select(expr);
I finally found a way:
Expression<Func<E,Datetime?>> expr =
e => (e.d1 ?? e.Rel.d1) ?? e.d2;
Expression<Func<E,someDeclaredType>> sel =
x => new someDeclaredType {
Id = x.Id,
V = expr.Compile()(x)
};
var q = myContext.E_DbSet.Select(sel.Compile());
another coding is (from #Anthony):
Expression<Func<E, someDeclaredType>> sel =
e => new someDeclaredType()
{
Id = e.Id,
V = expr.Compile()(e)
};
q = Files.Select(sel.Compile());
by the sample: dotnetfiddle
Please not that the code is designed for linq to entities. It may fire exception in linq to object due to Rel = null.
I've read many great answers here on stack about dynamic creation of lambda expression, but cannot make my own.
I need to select entity from DB by ID, but I don't know its type beforehand. Only name of a type. So:
var modelType = Assembly.Load("Models").GetTypes().First(t => t.Name == type + "Model");
MethodInfo method = typeof(CommonRepository).GetMethod("GetByExpression",
BindingFlags.Instance | BindingFlags.Public);
var arg = Expression.Constant(null, modelType);
var body = Expression.Convert(Expression.PropertyOrField(arg, "ID"),
typeof(int));
var lambda = Expression.Lambda<Func<object, bool>>(body);
var model = method.Invoke(this, new object[] { lambda });
var field = modelType.GetProperty("Disable", BindingFlags.Instance);
field.SetValue(model, false);
this.marathonRepository.SaveOrUpdate(model);
I guess in my code I've made "e.ID" part of lambda. How to make "== id" part?
I try to break the expression up into its parts, using comments to help understand the expression to be built.
Consider the following...
// id
var id = 2;
var idConstant = Expression.Constant(id);
// (object e) => ...
var param = Expression.Parameter(typeof(object));
// (object e) => ((modelType)e).ID
var property = Expression.PropertyOrField(Expression.Convert(param, modelType), "ID"));
// (object e) => ((modelType)e).ID == id
var body = Expression.Equal(property, idConstant);
var lambda = Expression.Lambda<Func<object, bool>>(body, param);
I was trying to generate a simple Lambda Expression at runtime with no luck... something like this:
var result = queryableData.Where(item => item.Name == "Soap")
Here is my example class and a fixture queryable:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
IQueryable<Item> queryableData = ...;
Then I generate a lambda expression at runtime correct code follows:
//"item" in "item =>..."
var item = Expression
.Parameter(typeof(Item), "item");
//property of my item, this is "item.Name"
var prop = Expression
.Property(item, "Name");
//then "Soap" in '... => item.Name=="Soap"'
var value = Expression.Constant("Soap");
//equality expression "==" in my primer
var equals = Expression.Equal(prop, value);
//then lambda
var lambda = Expression.Lambda<Func<Item, bool>>(equals, item);
//and here are the results
var results = queryableData.Where(lambda);
Big thanks to dtb for advice!
In the following query
var result = query.Where(item => item.Name == "Soap")
the lambda expression is
item => item.Name == "Soap"
You only need to construct this part, not the Where call which accepts an expression tree.
The expression tree for the lambda expression looks like this:
Lambda
/ \
Equal Parameter
/ \ item
Property \
"Name" Constant
| "Soap"
Parameter
item
In code:
var item = Expression.Parameter(typeof(Item), "item");
var prop = Expression.Property(item, "Name");
var soap = Expression.Constant("Soap");
var equal = Expression.Equal(prop, soap);
var lambda = Expression.Lambda<Func<Item, bool>>(equal, item);
var result = queryableData.Where(lambda);