When ToList is applied always this exception is thrown
Value cannot be null. Parameter name: querySource
var memberExpression = (navigationPropertyPath.Body as MemberExpression);
var returnType = memberExpression.Member is MethodInfo? ((MethodInfo)memberExpression.Member).ReturnType: ((PropertyInfo)memberExpression.Member).PropertyType;
var parameter = Expression.Parameter(typeof(TEntity), "s");
var fieldParameter = Expression.Parameter(returnType.GetTypeInfo().GetGenericArguments()[0], "field");
var anyPredicate = Expression.Lambda(
Expression.NotEqual(
Expression.PropertyOrField(fieldParameter, "Column"),
Expression.Constant(true)
),
fieldParameter);
var fieldCondition = Expression.Call(
typeof(Enumerable).GetMethods().Where(x=>x.Name == "Any").Skip(1).Take(1).First().MakeGenericMethod(new[] { fieldParameter.Type }),
memberExpression,
anyPredicate);
// You can use the fieldCondition in your combinator,
// the following is just to complete the example
var predicate = Expression.Lambda<Func<TEntity, bool>>(fieldCondition, parameter);
var list= source.Where(predicate).ToList(); ;
This code creates a custom standard WHERE query and tries to convert the result to a list.
Does anyone know how to fix this?
Related
I've created an extension on IQueryable as I would like to order by nullable datetimes first then order by the datetime itself using just the string of the property i.e "activeTo". I've created the code below:
public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortBy)
{
//create the expression tree that represents the generic parameter to the predicate
var param = Expression.Parameter(typeof(T), "p");
//create an expression tree that represents the expression p=>p.SortField.HasValue
var prop = Expression.Property(param, sortBy);
var target = Expression.Constant(null, prop.Type);
var bin = Expression.Equal(prop, Expression.Convert(target, prop.Type));
var exp = Expression.Lambda(bin, param);
string method = "OrderBy";
Type[] types = new Type[] { source.ElementType, exp.Body.Type };
var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp);
//now do the ThenBy bit,sending in the above expression to the Expression.Call
exp = Expression.Lambda(prop, param);
types = new Type[] { source.ElementType, exp.Body.Type };
method = "ThenBy";
var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp);
return source.Provider.CreateQuery<T>(ThenByCallExpression);
}
This extension is called by:
query.Sort("activeTo");
Which then gives the below the response:
{
"title": "test 5",
"activeFrom": "2019-06-08T21:26:50.2833333",
"activeTo": "2019-06-08T21:26:50.2833333",
},
{
"title": "test 2",
"activeFrom": "2019-06-08T21:28:45.65",
"activeTo": null,
}
I'd expect the record with activeTo as null to be first however, this isn't the case.
Does anyone know what I'm doing wrong?
From the comments the goal seems to be to dynamically generate an expression which sorts null values to the front.
The current code produces the following expression OrderBy(p => p.activeTo == null).ThenBy(p => p.activeTo == null). This has two flaws:
It sorts null values to the front as the bools sort order is false, true (as their ordinal values are 0 and 1, respectively). Therefore a comparison to null first collects the false cases, and then the truecases.
The ThenBy repeats the OrderBy, but was actually intended to emit ThenBy(p => p.ActiveTo).
The first can be solved by either using Expression.NotEqual instead of Expression.Equal for p => p != p.activeTo or by using OrderByDescending instead of OrderBy.
In total the code should be:
public static IQueryable<T> Sort<T>(IQueryable<T> source, string sortBy)
{
//create the expression tree that represents the generic parameter to the predicate
var param = Expression.Parameter(typeof(T), "p");
//create an expression tree that represents the expression p=>p.SortField.HasValue
var prop = Expression.Property(param, sortBy);
var target = Expression.Constant(null, prop.Type);
// NotEqual, to sort nulls before not-nulls
var bin = Expression.NotEqual(prop, Expression.Convert(target, prop.Type));
var exp = Expression.Lambda(bin, param);
// OrderBy with the null comparison expression
string method = nameof(Queryable.OrderBy);
Type[] types = new Type[] { source.ElementType, exp.Body.Type };
var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp);
// ThenBy with the property expression
exp = Expression.Lambda(prop, param);
types = new Type[] { source.ElementType, exp.Body.Type };
method = nameof(Queryable.ThenBy);
var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp);
return source.Provider.CreateQuery<T>(ThenByCallExpression);
}
This yields the following expression:
OrderBy(p => p.activeTo != null).ThenBy(p => p.activeTo).
Remarks: It should be noted that usually OrderBy(p => p.activeTo) would already sort null values to the front, as this is the default sort order for strings, nullables, and so on. However, this behavior could be overwritten depending by the specific type and depend on the query source. Therefore, I left it like the OP has.
I have an object called SearchDetails which contains:
SearchDetails:
{ ColName: "StrName"
SearchVal" "mega" }
I am making a generic lambda expression by using reflection method.
public dynamic searchMethod(object SearchDetails)
{
ParameterExpression Parameter = Expression.Parameter(typeof(SearchDetails), "x");
var searchCol = Expression.Property(
Parameter,
SearchDetails.GetType().GetProperty("ColName")
);
var colVal = Expression.Property(
Parameter,
SearchDetails.GetType().GetProperty("SearchValue").Name
);
Expression contMethod = Expression.Call(searchCol, "Contains", null, colVal);
Expression<Func<SearchDetails, bool>> lambda =
Expression.Lambda<Func<SearchDetails, bool>>(contMethod, Parameter);
return lambda;
}
The problem is that I am getting lambda expressions as follow:
{x => x.ColName.Contains(x.SearchValue)}
However, I want it to be like this: {x => x.StrName.Contains("megabrand")}.
I cannot access the value of the properties: ColName and SearchValue.
How to solve this problem?
What you are looking for is probably something similar to this:
public static Expression<Func<TSource, bool>> SearchMethod<TSource>(SearchDetails searchDetails)
{
ParameterExpression par = Expression.Parameter(typeof(TSource), "x");
var col = Expression.Property(par, searchDetails.ColName);
Expression body = Expression.Call(col, "Contains", null, Expression.Constant(searchDetails.SearchVal));
var lambda = Expression.Lambda<Func<TSource, bool>>(body, par);
return lambda;
}
Note that you have to pass the type of your table somewhere, in this case as a generic parameter TSource.
Use it like:
var search = new SearchDetails
{
ColName = "Foo",
SearchVal = "Bar",
};
var lambda = SearchMethod<TbStore>(search);
As an alternative you could use System.Linq.Dynamic.Core to obtain something similar.
var result = db.Where(searchDetails.ColName + ".Contains(#0)", searchDetails.SearchVal);
I'm making a lambda expression like this :
var property = typeof(Customer).GetProperty(inputArray[0], BindingFlags.Instance | BindingFlags.Public);
var parameter = Expression.Parameter(typeof(Customer));
var memberExpression = Expression.Property(parameter, property);
var eq = Expression.Equal(memberExpression, Expression.Constant(value));
//Combining eq with ANDs and ORs
var lambdaExpression = Expression.Lambda<Func<Customer, bool>>(eq, parameter);
// the lambda expression looks like this : {Param_0 => ((Param_0.NAME== "JASON") And (Param_0.NAME == "JASON"))}
var filteredCustomers = db.Customer.Where(lambdaExpression);
var list = filteredCustomers.ToList();
I can see that there are records whose name is JASON in the db. But list count is always zero. Can you tell me what the problem is? Thanks in advance.
For future visitors looking at this post,OP has trimmed the value removing extra spaces.
var param = Expression.Parameter(typeof(Customer), "p");
var memberExpression = Expression.PropertyOrField(param, "Your_Property_Name");
var body = Expression.Equal(memberExpression, Expression.Constant(value.Trim()));
var lambda = Expression.Lambda(body, param);
I'm facing a problem with executing a query against a Queryable dataset.
The original call looks like this:
books = books.Where(b => (GetPropertyValue(b, filter.CategoryProperties.DbName) == null ? 0 : Convert.ToInt32(GetPropertyValue(b, filter.CategoryProperties.DbName))) < Convert.ToInt32(filter.Value));
This gets me the Method not recognized error.
This of course is expected due to the call to GetPropertyValue. I then read that I should build the expression tree myself.
That result in the following code:
public IQueryable<Books> GetExpression(IQueryable<Books> books, BookCategoryMapping filter)
{
var booksExpression = Expression.Parameter(typeof(Books), "b");
var methodInfo = this.GetType().GetMethod("GetPropertyValue");
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
var left = Expression.Constant(value);
var right = Expression.Constant(filter.Value);
var expression = Expression.Equal(left, right);
var whereExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { books.ElementType }, books.Expression, Expression.Lambda<Func<Books, bool>>(expression, new ParameterExpression[] { booksExpression }));
return books.Provider.CreateQuery<Books>(whereExpression);
}
Only problem being, that I get the same error. It seems like the following line only produces an expression and not the value of said expression.
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
Any help producing the correct expression tree would be greatly appreciated :-)
EDIT:
Here's the GetPropertyValue method:
public static object GetPropertyValue(object obj, string name)
{
try
{
return obj.GetType().GetProperty(name)?.GetValue(obj, null);
}
catch (Exception ex)
{
LogManager.Log(LogLevel.Error, null, ex);
}
return obj;
}
The method below generates an Expression<Func<Book, bool>> which determines whether a given property of a Book is less than a given (constant) value. The error-checking code you currently have in GetPropertyValue can probably be replaced by catching the ArgumentException which will be thrown when you try to create an expression for a nonexistent property.
Note that I have assumed that the property you're accessing is genuinely numeric, and that your call to Convert.ToInt32 was only necessary because your GetPropertyValue method returns an object.
Expression<Func<Book, bool>> GenerateLessThanExpression(string propertyName, int value)
{
var parameter = Expression.Parameter(typeof (Book));
var property = Expression.Property(parameter, propertyName);
var comparison = Expression.LessThan(property, Expression.Constant(value));
return Expression.Lambda<Func<Book, bool>>(comparison, parameter);
}
Here's a sample usage to return only very short books:
var filter = GenerateLessThanExpression("Pages", 5);
var filtered = books.Where(filter);
I have this code :
var listExpression = new List<Expression>();
var parameter = Expression.Parameter(typeof(T));
var memberExpression = Expression.PropertyOrField(parameter, MyProperty);
listExpression.Add(
Expression.Call(
((MemberExpression)memberExpression), "Contains", null,
Expression.Constant((string)MyValue))
);
Expression body = Expression.Constant(true);
foreach (var expression in listExpression)
body = Expression.And(body, expression);
return Expression.Lambda<Func<T, bool>>(body, parameter);
The result of this is :
"True & $var1.AGE >= 5"
I don't have any problem when I use this predicate with Entity Framework but not work with NHiernate. I think the problem is the "True". Is it possible to create this predicate without this "True" ?
replace
Expression body = Expression.Constant(true);
foreach (var expression in listExpression)
body = Expression.And(body, expression);
by
var body = listExpression.First();//check first if listExpression.Any() would be better
listExpression.Skip(1).Aggregate(body, Expression.And);