Let's say I have defined the following variables:
IQueryable<MyClass> myQueryable;
Dictionary<string, Expression<Func<MyClass, bool>>> extraFields;
// the dictionary is keyed by a field name
Now, I want to tack on some dynamic fields to the IQueryable, so that it returns an IQueryable<ExtendedMyClass>, where ExtendedMyClass is defined as:
class ExtendedMyClass
{
public MyClass MyObject {get; set;}
public IEnumerable<StringAndBool> ExtraFieldValues {get; set;}
}
class StringAndBool
{
public string FieldName {get; set;}
public bool IsTrue {get; set;}
}
In other words, for every value in extraFields, I want to have a value in ExtendedMyClass.ExtraFieldValues representing whether that expression evaluates to True or not for that row.
I have a feeling this should be doable in dynamic Linq and LinqKit, though I've never used that seriously before. I'm also open to other suggestions, especially if this can be done in good ol' strong-typed Linq.
I am using Linq to Entities, so the query needs to translate to SQL.
So, we'll have a lot of steps here, but each individual step should be fairly short, self-contained, reusable, and relatively understandable.
The first thing we'll do is create a method that can combine expressions. What it will do is take an expression that accepts some input and generates an intermediate value. Then it will take a second expression that accepts, as input, the same input as the first, the type of the intermediate result, and then computes a new result. It will return a new expression taking the input of the first, and returning the output of the second.
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
To do this we simply replace all instances of the second parameter in the second expression's body with the body of the first expression. We also need to ensure both implementations use the same parameter instance for the main parameter.
This implementation requires having a method to replace all instances of one expression with another:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
Next we'll write a method that accepts a sequences of expressions that accept the same input and compute the same type of output. It will transform this into a single expression that accepts the same input, but computes a sequence of the output as a result, in which each item in the sequence represents the result of each of the input expressions.
This implementation is fairly straightforward; we create a new array, use the body of each expression (replacing the parameters with a consistent one) as each item in the array.
public static Expression<Func<T, IEnumerable<TResult>>> AsSequence<T, TResult>(
this IEnumerable<Expression<Func<T, TResult>>> expressions)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.NewArrayInit(typeof(TResult),
expressions.Select(selector =>
selector.Body.Replace(selector.Parameters[0], param)));
return Expression.Lambda<Func<T, IEnumerable<TResult>>>(body, param);
}
Now that we have all of these general purpose helper methods out of the way, we can start working on your specific situation.
The first step here is to turn your dictionary into a sequence of expressions, each accepting a MyClass and creating a StringAndBool that represents that pair. To do this we'll use Combine on the value of the dictionary, and then use a lambda as the second expression to use it's intermediate result to compute a StringAndBool object, in addition to closing over the pair's key.
IEnumerable<Expression<Func<MyClass, StringAndBool>>> stringAndBools =
extraFields.Select(pair => pair.Value.Combine((foo, isTrue) =>
new StringAndBool()
{
FieldName = pair.Key,
IsTrue = isTrue
}));
Now we can use our AsSequence method to transform this from a sequence of selectors into a single selector that selects out a sequence:
Expression<Func<MyClass, IEnumerable<StringAndBool>>> extrafieldsSelector =
stringAndBools.AsSequence();
Now we're almost done. We now just need to use Combine on this expression to write out our lambda for selecting a MyClass into an ExtendedMyClass while using the previous generated selector for selecting out the extra fields:
var finalQuery = myQueryable.Select(
extrafieldsSelector.Combine((foo, extraFieldValues) =>
new ExtendedMyClass
{
MyObject = foo,
ExtraFieldValues = extraFieldValues,
}));
We can take this same code, remove the intermediate variable and rely on type inference to pull it down to a single statement, assuming you don't find it too unweidly:
var finalQuery = myQueryable.Select(extraFields
.Select(pair => pair.Value.Combine((foo, isTrue) =>
new StringAndBool()
{
FieldName = pair.Key,
IsTrue = isTrue
}))
.AsSequence()
.Combine((foo, extraFieldValues) =>
new ExtendedMyClass
{
MyObject = foo,
ExtraFieldValues = extraFieldValues,
}));
It's worth noting that a key advantage of this general approach is that the use of the higher level Expression methods results in code that is at least reasonably understandable, but also that can be statically verified, at compile time, to be type safe. There are a handful of general purpose, reusable, testable, verifiable, extension methods here that, once written, allows us to solve the problem purely through composition of methods and lambdas, and that doesn't require any actual expression manipulation, which is both complex, error prone, and removes all type safety. Each of these extension methods is designed in such a way that the resulting expression will always be valid, so long as the input expressions are valid, and the input expressions here are all known to be valid as they are lambda expressions, which the compiler verifies for type safety.
I think it's helpful here to take an example extraFields, imagine how would the expression that you need look like and then figure out how to actually create it.
So, if you have:
var extraFields = new Dictionary<string, Expression<Func<MyClass, bool>>>
{
{ "Foo", x => x.Foo },
{ "Bar", x => x.Bar }
};
Then you want to generate something like:
myQueryable.Select(
x => new ExtendedMyClass
{
MyObject = x,
ExtraFieldValues =
new[]
{
new StringAndBool { FieldName = "Foo", IsTrue = x.Foo },
new StringAndBool { FieldName = "Bar", IsTrue = x.Bar }
}
});
Now you can use the expression trees API and LINQKit to create this expression:
public static IQueryable<ExtendedMyClass> Extend(
IQueryable<MyClass> myQueryable,
Dictionary<string, Expression<Func<MyClass, bool>>> extraFields)
{
Func<Expression<Func<MyClass, bool>>, MyClass, bool> invoke =
LinqKit.Extensions.Invoke;
var parameter = Expression.Parameter(typeof(MyClass));
var extraFieldsExpression =
Expression.Lambda<Func<MyClass, StringAndBool[]>>(
Expression.NewArrayInit(
typeof(StringAndBool),
extraFields.Select(
field => Expression.MemberInit(
Expression.New(typeof(StringAndBool)),
new MemberBinding[]
{
Expression.Bind(
typeof(StringAndBool).GetProperty("FieldName"),
Expression.Constant(field.Key)),
Expression.Bind(
typeof(StringAndBool).GetProperty("IsTrue"),
Expression.Call(
invoke.Method,
Expression.Constant(field.Value),
parameter))
}))),
parameter);
Expression<Func<MyClass, ExtendedMyClass>> selectExpression =
x => new ExtendedMyClass
{
MyObject = x,
ExtraFieldValues = extraFieldsExpression.Invoke(x)
};
return myQueryable.Select(selectExpression.Expand());
}
Related
Here is the problem:
I want to write extension method for getting property expression of collection field.
Here is an example.
I have the following class:
class Foo
{
public int Id {get; set;}
public List<Foo> Collection {get; set;}
}
And I have the following code:
Expression<Func<Foo, object>> expression = x => x.Collection.First().Id;
And I want to create an extension to get the same result expression with following signature:
Expression<Func<Foo, object>> expression = x => x.Collection.Field(foo => foo.Id);
static Func<Foo, object> Field(this List<Foo> people, Expression<Func<Foo, object>> accessor)
{
// implementation goes here
}
The main purpose of it is to generate path to property. In this case we need to get the following result: "Collection.Id". I already have method to convert Expression to property path. The Field method is an extension method, which I want. It should return the same result like x => x.Collection.First().Id but with the following signature: x => x.Collection.Field(foo => foo.Id)
Is there any way to do this?
Thanks a lot.
Since no methods are executed when assigning a lambda to an Expression variable, it isn't possible to have a method as you describe work.
What you can do is create a method that takes in the collection expression and the property expression and creates a new Expression lambda that has the desired result.
Expression<Func<T, TProp>> Field<T, TProp>(Expression<Func<T, IEnumerable<T>>> collectionEFn, Expression<Func<T, TProp>> pathEFn) {
// (Foo x)
var parm = collectionEFn.Parameters[0];
// x.Collection
var collectionExpr = collectionEFn.Body;
// y.Id
var pathExpr = (MemberExpression)pathEFn.Body;
// x.Collection.First()
var firstExpr = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, collectionExpr);
// x.Collection.First().Id
var newBody = Expression.Property(firstExpr, pathExpr.Member.Name);
// (Foo x) => x.Collection.First().Id
return Expression.Lambda<Func<T, TProp>>(newBody, parm);
}
With this method defined, you can call it as so:
var expr = Field((Foo x) => x.Collection, x => x.Id);
and the resulting value for expr will be an Expression tree representing x => x.Collection.First().Id
Note that the resulting type has the result type of the property referenced; if you want object, you would need to add a call to Convert to convert the PropertyExpression to the object type.
If you insist on doing so (like the others I am not still clear about your goal of doing this), you can create a generic extension method that takes a lambda expression as a parameter, and returns an expression. Something like this:
public static class MyExtensions
{
public static Expression<Func<T, object>> Field<T, TPropert>(
this IEnumerable<T> collection,
Expression<Func<T, TPropert>> field)
{
var prm = Expression.Parameter(typeof(IEnumerable<T>), "x");
var first = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, prm);
var body = Expression.Property(first, ((MemberExpression)field.Body).Member.Name);
return Expression.Lambda<Func<T, object>>(body, field.Parameters);
}
}
I have a sorting/filtering class that takes an IEnumerable<T> for data and a object that contains the sorting/filtering conditions.
I am building expressions to then execute over the collection.
ParameterExpression parameter = Expression.Parameter(typeof(T));
Expression left = splitFilter.Aggregate((Expression)parameter, Expression.PropertyOrField);
var constant = Expression.Constant(filter.Value.Text.ToLower(), typeof(string));
var body = Expression.Call(MakeString(left, constant), "Contains", Type.EmptyTypes, constant);
Expression bodyNullCheck = CheckForNullableParameters(left, body);
Expression<Func<T, bool>> ex = Expression.Lambda<Func<T, bool>>(bodyNullCheck, parameter);
Example data structure:
public class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public List<StoreItem> BoughtItems {get; set;}
}
public class StoreItem
{
public int Id {get; set;}
public string Name {get; set;}
}
The solution works great for things that have a depth of 1 for example:
Here we can easily retrieve all Customers that have "t" in their name.
The problem comes when we wish to get all customers which have bought an item which contains "Bag" in the name.
From the front-end, I'm currently getting "BoughtItems.Name" but I can't seem to get into the "StoreItem" object even though I have the exact property name.
string[] splitFilter = filter.Key.Split('.'); // Contains {"BoughtItems", "Name"}
Expression parent = new string[] { splitFilter[0] }.Aggregate((Expression)parameter, Expression.PropertyOrField);
Expression child = new string[] { splitFilter[1] }.Aggregate(parent, Expression.PropertyOrField);
Currently, I'm trying to get the expressions for both objects separately and then to somehow combine them, but if I try to get the type of the first expression it says it can't access property Name of type Collection. (duuh)
Is there a way in C# to say "The items inside this Generic collection have this property, now using this property let's go down 1 layer and get another property?" (StoreItem.Name)
The result I'm trying to get should be semi-generic and give the same result as the example below, without using reflection.
string searchBy = "test";
var result = MyCustomers.Where(x => x.BoughtItems.Any(y => y.Name.Contains(searchBy))).ToList()
As a personal preference, I like explicit over implicit. So unless you have an un-maintainable amount of different cases coming from front-end, you should implement each case explicitly.
Let us get back to the question at hand.
You would like to achieve the following expression tree from my understanding:
Expression<Func<Customer, bool>> exp =
myCustomer => myCustomer.BoughtItems.Any(storeItem => storeItem.Name.Contains("test"));
I like commenting my code extensively so from here on most of the time comments will do the explaining :)
var myCustomerParameter = Expression.Parameter(typeof(Customer), "myCustomer");
var storeItemParameter = Expression.Parameter(typeof(StoreItem), "storeItem");
// This expression tree creates the following lambda
// storeItem => storeItem.Name.Contains("test")
var innerLambda = Expression.Lambda<Func<StoreItem, bool>>(
Expression.Call(
// storeItem.Name
Expression.Property(storeItemParameter, "Name"),
// storeItem.Name.Contains
MethodSelectorExtensions.MethodSelector<string>("Contains", new[] {typeof(string)}),
// storeItem.Name.Contains("test")
Expression.Constant("test", typeof(string))
),
storeItemParameter
);
// This expression tree creates the following lambda
// myCustomer => Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test")
var finalLambda = Expression.Lambda<Func<Customer, bool>>(
Expression.Call(
// Since LINQ is extensions methods, it is static method, no instance is required
null,
// Enumerable.Any
MethodSelectorExtensions.LinqMethodSelector("Any", new[] {typeof(IEnumerable<StoreItem>), typeof(Func<StoreItem, bool>)}),
// Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test"))
Expression.Property(myCustomerParameter, "BoughtItems"), innerLambda
),
myCustomerParameter
);
Utility methods for creating Expressions
public static class MethodSelectorExtensions {
/// <summary>
/// Cached delegate to be used in typeof(Enumerable) methods
/// </summary>
private static readonly Func<string, Type[], MethodInfo> EnumerableMethodSelector;
/// <summary>
/// Static ctor to cache EnumerableMethodSelector function
/// </summary>
static MethodSelectorExtensions() {
var genericMethodSelector = typeof(MethodSelectorExtensions).GetMethod(nameof(MethodSelector), new[] {typeof(string), typeof(Type[])});
// C# does not allow for static type generic methods but we can create through type system ;)
EnumerableMethodSelector = (Func<string, Type[], MethodInfo>) genericMethodSelector.MakeGenericMethod(typeof(Enumerable)).CreateDelegate(typeof(Func<string, Type[], MethodInfo>));
}
public static MethodInfo MethodSelector<TType>(string nameOfMethod, Type[] functionParameterTypes) {
return typeof(TType)
.GetMethods()
.Where(n => n.Name == nameOfMethod)
// in case we have TType as Enumerable, we will need to extract generic parameter
.Select(n => n.IsGenericMethod ? n.MakeGenericMethod(functionParameterTypes[0].GenericTypeArguments[0]) : n)
.Single(n => n.GetParameters().Select(p => p.ParameterType).SequenceEqual(functionParameterTypes));
}
public static MethodInfo LinqMethodSelector(string nameOfMethod, Type[] functionParameterTypes) {
return EnumerableMethodSelector(nameOfMethod, functionParameterTypes);
}
}
And let us have a final look at the running program:
Here's my scenario:
There is a collection of objects where each object contains a Dictionary<string, string>. The user can build a set of queries for this collection from another app to obtain a subset by selecting a Key in the Dictionary, an operator such as > or CONTAINS, etc., and a Value. They can also balance parenthesis to create groups of queries and select AND/OR operators to combine the queries.
As an example, let's say I have a collection of Car objects and the Dictionary contains keys for Make, Model, and Year.
My app is getting these queries in the form of a string like so:
"((Make = Honda) AND (Model CONTAINS Civic)) || (Year >= 2015)"
This tells me that from the collection of Car objects that I want cars that have Dictionary keys/values of <Make, Honda> and <Model, anything that contains "Civic"> OR <Year, greater than or equal to 2015>
So, I parse these out and put them into a QueryClass containing three string fields for the Key, the Operator, and the Value. I also keep track of the operator between the queries, and if they are in a group of parentheses or not.
Currently, I have to go through each QueryClass one by one performing the query, checking what the previous operator was, if it's part of a group, etc. and combining collections over and over until it reaches the end. This is tedious and seems like an awful way to do things. If there was a way to build these LINQ queries dynamically or perform SQL statements (what these are essential) on this collection it would be better.
Here's my query class that I'm storing the parsed strings in:
class QueryClass
{
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput)
{
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null)
{
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
}
My parsing class is pretty long so I won't post the whole thing but it returns a List<object> where each element is either:
A QueryClass
"AND" or "OR"
Another List which means it's a group of queries that were grouped by parentheses, containing the two choices above.
Here's an example of the List<object> I get after parsing a string:
I then just loop through each element, determine if the value is a double or string, and execute a LINQ statement on my collection. I'm checking if the operator was "AND" or "OR" (or none if it's just one query), if it's part of a group or not, and combining the results appropriately.
Here is my implementation of converting your query into a Func. Since I wasn't sure what type was in your collection, I made an interface to represent objects that had an attributes Dictionary<string, string> and processed that.
Basically I added a method to QueryClass to convert it to an Expression. It uses a helper dictionary string->lambda that builds the appropriate comparison Expression for each operator.
Then I added a class to convert the List<object> into a Func<IItem,bool> suitable for a LINQ Where filter.
public interface IItem {
Dictionary<string, string> attributes { get; set; }
}
class QueryClass {
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput) {
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null) {
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
static MethodInfo getItemMI = typeof(Dictionary<string, string>).GetMethod("get_Item");
static Dictionary<string, Func<Expression, Expression, Expression>> opTypes = new Dictionary<string, Func<Expression, Expression, Expression>> {
{ "==", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.Equal, lhs, rhs) },
{ ">=", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, Expression.Call(lhs, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), rhs), Expression.Constant(0)) },
{ "CONTAINS", (Expression lhs, Expression rhs) => Expression.Call(lhs, typeof(String).GetMethod("Contains"), rhs) }
};
static MemberInfo attribMI = typeof(IItem).GetMember("attributes")[0];
public Expression AsExpression(ParameterExpression p) {
var dictField = Expression.MakeMemberAccess(p, attribMI);
var lhs = Expression.Call(dictField, getItemMI, Expression.Constant(FieldName));
var rhs = Expression.Constant(Value);
if (opTypes.TryGetValue(Operator, out var exprMakerFn))
return exprMakerFn(lhs, rhs);
else
throw new InvalidExpressionException($"Unrecognized operator {Operator}");
}
}
public class LinqBuilder {
static Type TItems = typeof(IItem);
static Expression BuildOneLINQ(object term, ParameterExpression parm) {
switch (term) {
case QueryClass qc: // d => d[qc.FieldName] qc.Operator qc.Value
return qc.AsExpression(parm);
case List<object> subQuery:
return BuildLINQ(subQuery, parm);
default:
throw new Exception();
}
}
static Expression BuildLINQ(List<object> query, ParameterExpression parm) {
Expression body = null;
for (int queryIndex = 0; queryIndex < query.Count; ++queryIndex) {
var term = query[queryIndex];
switch (term) {
case string op:
var rhs = BuildOneLINQ(query[++queryIndex], parm);
var eop = (op == "AND") ? ExpressionType.AndAlso : ExpressionType.OrElse;
body = Expression.MakeBinary(eop, body, rhs);
break;
default:
body = BuildOneLINQ(term, parm);
break;
}
}
return body;
}
public static Func<IItem, bool> BuildLINQ(List<object> query) {
var parm = Expression.Parameter(TItems, "i");
return Expression.Lambda<Func<IItem, bool>>(BuildLINQ(query, parm), parm).Compile();
}
}
Once you have this, you can pass in a List<object> expression and then filter your collection. Given a query q and a collection of IItems cs, you can do:
var ans = cs.Where(LinqBuilder.BuildLINQ(q));
I would approach this problem little differently, since what you are already having is List<object>, which internally contain a QueryClass containing all the relevant fields containing information, FieldName,Operator and Value, where you are aware which of the binary expressions have to be bundled in a parentheses. Important point is how can you create a Run-time Expression to take care of all kinds of scenarios.
Following is a sample to mimic your scenario:
Sample Class
public class Car
{
public string Make {get; set;}
public string Model {get; set;}
public int Year {get; set;}
}
Query
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic")) Or (c.Year >= 2015))
Linqpad Code
void Main()
{
var cars = new List<Car>();
Expression<Func<Car,bool>> e1 = c => c.Make.Equals("Honda");
Expression<Func<Car,bool>> e2 = c => c.Model.Contains("Civic");
Expression<Func<Car,bool>> e3 = c => c.Year >= 2015;
var expr1 = Expression.AndAlso(e1.Body,e2.Body);
var expr2 = e3;
var finalExpression = Expression.Or(expr1,expr2.Body);
finalExpression.Dump();
}
Purpose
As it can be seen that I have manually constructed the Expressions and finally Dump the final expression, since in Linqpad it provides a graphical representation of how shall the Expression be constructed dynamically, overall image is too big and deep to be pasted here (you may try yourself using LinqPad), but following are the relevant details:
Create a ParameterExpression, this acts as a lambda parameter representing the Car class object (this is independent of the fields of the Query class)
var parameterExpression = Expression.Parameter(typeof(Car),"c");
Create MemberExpression to access each relevant field of the Car class, which is used in the Query (this one needs Field property of the Query class)
var makeMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Make"));
var modelMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Model"));
var yearMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Year"));
Completing the Expression:
a.) c => c.Make.Equals("Honda") we create as follows: (this one needs Value property of the QueryClass)
var makeConstantExpression = Expression.Constant("Honda");
var makeEqualExpression = Expression.Equal(makeMemberAccessExpression, makeConstantExpression);
b.) c.Model.Contains("Civic") can be represented as follows here we need supply the MethodInfo for the string Contains method and create a MethodCallEXpression
var modelConstantExpression = Expression.Constant("Civic");
var stringContainsMethodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var modelContainsMethodExpression = Expression.Call(modelMemberAccessExpression, stringContainsMethodInfo, modelConstantExpression);
c.) c.Year >= 2015 can simply projected as:
var yearConstantExpression = Expression.Constant(2015);
var yearGreaterThanEqualExpression = Expression.GreaterThanOrEqual(yearMemberAccessExpression, yearConstantExpression);
Combining all together to form the Composite Expression:
Expressions a.) and b.) are combined together as follows:
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic"))
var firstExpression = Expression.AndAlso(makeEqualExpression,modelContainsMethodExpression);
Expression c.) is independent:
c.Year >= 2015
var secondExpression = yearGreaterThanEqualExpression;
Final Combined Expression and Creation a Func Delegate
// Expressions combined via Or (||)
var finalCombinedExpression = Expression.Or(firstExpression,secondExpression);
// Create Lambda Expression
var lambda = Expression.Lambda<Func<Car,bool>>(finalCombinedExpression, parameterExpression);
// Create Func delegate via Compilation
var func = lambda.Compile();
func delegate thus can be used in any of the where clause in the Linq, which expects Func<Car,bool>
Design Suggestions
Using the explanation above and values from the Query Class placeholders or directly from the Dictionary it is feasible to create any number of Expressions to be used dynamically in the code, to be compiled and used as a Func delegate
Binary expressions like Equal, GreaterThan, LessThan, LessThanOrEqual,GreaterThanOrEqual are all exposed by the Expression trees to be used directly, For method like Contains available by default you need Reflection to get theb MethodInfo, similarly it can be done for the Static methods, just that there's object expressions
All Expressions expect values to be supplied Left to Right in the correct order, it cannot be random or incorrect order, else it will fail at run-time.
In your case, since you have few queries combined in parentheses and few independent, I would recommend creating multiple List<Expression>, where each List is combined in parentheses using AndAlso or OrElse and each list can thus be combined using And / Or
By this approach you shall be able to construct very complex requirements at runtime using Linq Expressions.
You should be able to use Linq Expressions (System.Linq.Expressions) and leverage predicates to handle your filtering.
public IQueryable<Car> GetCars(Expression<Func<Car, bool>> filter)
{
return context.Cars.Where(filter);
}
That said, the challenge will be to build your predicate expressions based off of your custom QueryClass object. To handle the filter on each Dictionary you can create a method to handle each:
public Expression<Func<Car, bool>> GetModelFilter(QueryClass modelQuery)
{
return modelQuery.Operator == "CONTAINS"? car => car.Model.Contains(modelQuery.Value) : car => car.Model == modelQuery.Value;
}
Considering you have a limited amount of filters, the above may be acceptable. However, when dealing with a large set this can also be done more dynamically, using reflection or a dynamic predicate builder but for simplicity you can follow the above.
HTH
I have an existing expression of type Expression<Func<T, object>>; it contains values like cust => cust.Name.
I also have a parent class with a field of type T. I need a method that accepts the above as a parameter and generates a new expression that takes the parent class (TModel) as a parameter. This will be used as an expression parameter of an MVC method.
Thus, cust => cust.Name becomes parent => parent.Customer.Name.
Likewise, cust => cust.Address.State becomes parent => parent.Customer.Address.State.
Here's my initial version:
//note: the FieldDefinition object contains the first expression
//described above, plus the MemberInfo object for the property/field
//in question
public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
where TModel: BaseModel<T>
{
var param = Expression.Parameter(typeof(TModel), "t");
//Note in the next line "nameof(SelectedItem)". This is a reference
//to the property in TModel that contains the instance from which
//to retrieve the value. It is unqualified because this method
//resides within TModel.
var body = Expression.PropertyOrField(param, nameof(SelectedItem));
var member = Expression.MakeMemberAccess(body, field.Member);
return Expression.Lambda<Func<TModel, object>>(member, param);
}
The error I'm currently receiving is when I have a field with multiple parts (i.e. cust.Address.State instead of just cust.Name). I get an error on the var member line that the specified member doesn't exist--which is true, since the body at that refers to the parent's child (Customer) and not the item that contains the member (Address).
Here's what I wish I could do:
public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
where TModel: BaseModel<T>
{
var param = Expression.Parameter(typeof(TModel), "t");
var body = Expression.PropertyOrField(param, nameof(SelectedItem));
var IWantThis = Expression.ApplyExpressionToField(field.Expression, body);
return Expression.Lambda<Func<TModel, object>>(IWantThis, param);
}
Any help getting to this point would be greatly appreciated.
Edit: This was marked as a possible duplicate of this question; however, the only real similarity is the solution (which is, in fact, the same). Composing expressions is not an intuitive solution to accessing nested properties via expressions (unless one's inuition is guided by certain experience, which should not be assumed). I also edited the question to note that the solution needs to be suitable for a paramter of an MVC method, which limits the possible solutions.
What you're looking for is the ability to compose expressions, just as you can compose functions:
public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
this Expression<Func<T, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
return Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
}
This relies on the following method to replace all instances of one expression with another:
public class ReplaceVisitor:ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression ex)
{
if(ex == from) return to;
else return base.Visit(ex);
}
}
public static Expression Replace(this Expression ex,
Expression from,
Expression to)
{
return new ReplaceVisitor(from, to).Visit(ex);
}
You can now take an expression selecting a property:
Expression<Func<Customer, object>> propertySelector = cust => cust.Name;
And an expression selecting that object from the model:
Expression<Func<CustomerModel, Customer>> modelSelector = model => model.Customer;
and compose them:
Expression<Func<Customer, object> magic = modelSelector.Compose(propertySelector);
How to convert : Expression<Func<T, object>> to Expression<Func<object>>?
For example:
public class Entity
{
public virtual long Id { get; set; }
}
Origin:
Expression<Func<Entity, object>> origin = x => x.Id;
Destination:
Entity alias = null;
Expression<Func<object>> destination = () => alias.Id;
Reason:
Actually I'm trying to create a custom cacheable ResultTransformer for Nhibernate. There is a method public QueryOverProjectionBuilder<T> WithAlias(Expression<Func<object>> alias); But in my classes for each particular entitites I would like to use more specific selector like Expression<Func<T, object>>
That's what Expression.Invoke is for.
Create a new lambda expression, and use Expression.Invoke on the original expression to compose the two expressions.
Sample:
Expression<Func<string, int>> inner = x => int.Parse(x);
var outer = Expression.Lambda<Func<int>>
(Expression.Invoke(inner, Expression.Constant("123")));
outer.Compile()().Dump(); // 123
Sadly, some expression parsers don't handle Invoke correctly - they assume it to be an invocation of a method, and reject it. In that case, you need to inline the expression. That means visiting the whole inner expression and replacing the ParameterExpressions with variables in the better case, or if parser doesn't support that either, inlining the argument in all the places.
You have two options:
Invoke the expression as Luaan suggested
Replace the parameters with the value you would like (a constant or a different expression)
Option 1 is as simple as Expression.Invoke but may not be compatible with libraries such as LINQ. Option 2 is best done using an expression visitor:
private class ExchangeParametersVisitor : ExpressionVisitor
{
public ParameterExpression Parameter { get; set; }
public Expression Value { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == Parameter)
{
return Value;
}
return node;
}
}
What you need to do is apply the visitor to the body of your lambda expression and use it to create a new lambda expression that contains all parameters as before except the one you replaced.