I am getting the following error when I ultimately try to run the query
Unknown LINQ expression of type 'IsFalse'
this is the code
private static IQueryable<T> QueryMethod<T>(
IQueryable<T> query,
QueryableRequestMessage.WhereClause.Rule rule,
Type type,
string methodName,
Expression property,
Expression value,
string op,
ParameterExpression parameter
) where T : class
{
var methodInfo = type.GetMethod(methodName, new[] { type });
var call = Expression.Call(property, methodInfo, value);
var expression = rule.Op.Equals(op)
? Expression.Lambda<Func<T, bool>>(call, parameter)
: Expression.Lambda<Func<T, bool>>(Expression.IsFalse(call), parameter);
query = query.Where(expression);
return query;
}
The important variables have the following values
query: an IQueryable that I am building up
type: String
methodName: "EndsWith"
rule.Op: "ne" //Not Ends With
op: "ew"
value: "somestring"
Basically, if op and rule.Op are equal, it just runs the methodName (EndsWith) and filters accordingly. However, If they are different, I want to negate the result.
It seems that you are not doing anything wrong; your LINQ provider simply does not know how to deal with the expression tree instance that Expression.IsFalse returns so it complains.
You can try to manually construct the "is false" expression tree yourself, which should work:
Expression.Lambda<Func<T, bool>>(
Expression.Equal(call, Expression.Constant(false)),
parameter)
Related
May I know why that error keeps pointing to "String isEmailVerified ...."?
public JsonResult GetMemberCounts([FromBody] ChartFilterRequest filter)
{
DateTime startDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "startDate").Value as string);
DateTime endDate = DateTime.Parse(filter.MainFilter.First(m => m.Name == "endDate").Value as string);
String isEmailVerified = filter.MainFilter.First(m => m.Name == "isEmailVerified").Value as string;
var data = _dashboardComponent.GetMemberCount(startDate, endDate, isEmailVerified);
return new JsonResult(data);
}
Try using FirstOrDefault and LastOrDefault instead of First and Last, these methods will return the default value of the type they are invoked for if no elements match the lambda expression you provide as a parameter.
In your project, You just use filter.MainFilter.First(xxxx) to select the data, So if no elements match the lambda expression you provide as a parameter,First() will throw an exception, So here will report this error.
Currently I am using expression builder for dynamic query generation.
I have created dynamic expressions for int, date time, and string operators. Now I am stuck at one point .
I want to compare month part of datetime through expression.
We are passing property of datetime and want to create expression for month of that property.
Code:
public static Expression GetDynamicquery<TSource>(
string property, object value, ParameterExpression p)
{
var propertyReference = Expression.Property(p, property);
var constantReference = Expression.Constant(value);
var expression = Expression.Equal(propertyReference, constantReference);
MethodInfo method = null;
return Expression.LessThanOrEqual(propertyReference, constantReference);
}
Here property is name of property which I am passing into Tsource.
p is parameter expression of type Tsource.
I want result like all birthdate of this month.
You can use
Expression.PropertyOrField method for that.
Not sure it would work with entity framework query though.
http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.propertyorfield(v=vs.110).aspx
Update
For entity framework you can use SqlFunctions.DatePart method.
http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions(v=vs.110).aspx
You would need an Expression.Call to represent that as an expression.
http://msdn.microsoft.com/en-us/library/bb349020(v=vs.110).aspx
I got answer of my question. What I did is like....
Code:
public static Expression GetDynamicquery<TSource>(
string property, object value, ParameterExpression p)
{
var propertyReference = Expression.Property(p, property);
propertyReference = Expression.Property(propertyReference, "Year");
var constantReference = Expression.Constant(value);
return Expression.Equal(propertyReference, constantReference);
}
Here first property reference will give datetime expression.
And again using that expression we can go one step inside and can access year property.
Same thing we can do for month.
I have a nice Linq challenge. I would like to filter some data on a Date using a FilterFunction. It should work like this:
ApplyDateFilterEx(query,null, DateTime.Today, s => s.CreatedDate);
Should filter the query so that all 'shipments' until today are returned. The query object is of IQueryable and the whole query should be evaluated by EF5 and therefore should convert to SQL.
This is what I have so far (DOES NOT COMPILE):
private static IQueryable<Shipment> ApplyDateFilterEx(IQueryable<Shipment> query, DateTime? minDate, DateTime? maxDate, Expression<Func<Shipment, DateTime?>> dateMember)
{
if (minDate != null)
{
//convert func to expression so EF understands
Expression<Func<Shipment, bool>> where = x => minDate <= dateMember(x);
query = query.Where(where);
}
if (maxDate != null)
{
Expression<Func<Shipment, bool>> where = x => dateMember(x) <= maxDate;
query = query.Where(where);
}
return query;
}
You can see I want to convert the expression s=>DateTime? to s=> Bool to be evaluated. How can I get this to work?
Thank you for reading.
Martijn
UDPATE:
I ended up with this (thanks to rdvanbuuren)
var predicate = Expression.Lambda<Func<Shipment, bool>>(
Expression.GreaterThanOrEqual(
selector.Body,
Expression.Constant(dateFilter.MinDate, typeof (DateTime?))
), selector.Parameters);
Have a look at
How to implement method with expression parameter c#
I think that's exactly what you need, but with an Expression.GreaterThanOrEqual or Expression.LessThanOrEqual. Good luck!
I am trying to write a really generic way to load EF entities in batches, using the Contains method to generate a SQL IN statement. I've got it working if I pass the entire expression in, but when I try to build the expression dynamically, I am getting a "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." So I know this means that EF thinks I'm calling an arbitrary method and it can't translate it into SQL, but I can't figure out how to get it to understand the underlying expression.
So If I do something like this (just showing the relevant snippets):
Function declaration:
public static List<T> Load<T>(IQueryable<T> entityQuery, int[] entityIds, Func<T, int> entityKey, int batchSize = 500, Func<T, bool> postFilter = null) where T : EntityObject
{
var retList = new List<T>();
// Append a where clause to the query passed in, that will use a Contains expression, which generates a SQL IN statement. So our SQL looks something like
// WHERE [ItemTypeId] IN (1921,1920,1922)
// See http://rogeralsing.com/2009/05/21/entity-framework-4-where-entity-id-in-array/ for details
Func<int[], Expression<Func<T, bool>>> containsExpression = (entityArray => (expr => entityArray.Contains(entityKey(expr))));
// Build a new query with the current batch of IDs to retrieve and add it to the list we are returning
newQuery = entityQuery.Where<T>(containsExpression(entityIds));
retList.AddRange(newQuery.ToList());
return retList;
}
Call function:
var entities = BatchEntity.Load<ItemType>(from eItemType in dal.Context.InstanceContainer.ItemTypes
select eItemType
, itemTypeData
, (ek => ek.ItemTypeId)
);
I get "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."
But if I change it to be this:
Function declaration:
public static List<T> Load<T>(IQueryable<T> entityQuery, int[] entityIds, Func<int[], Expression<Func<T, bool>>> containsExpression, int batchSize = 500, Func<T, bool> postFilter = null) where T : EntityObject
{
var retList = new List<T>();
// Build a new query with the current batch of IDs to retrieve and add it to the list we are returning
newQuery = entityQuery.Where<T>(containsExpression(entityIds));
retList.AddRange(newQuery.ToList());
return retList;
}
Call function:
var entities = BatchEntity.Load<ItemType>(from eItemType in dal.Context.InstanceContainer.ItemTypes
select eItemType
, itemTypeData
, (entityArray => (ek => entityArray.Contains(ek.ItemTypeId)))
);
It works fine. Is there any way I can make EF understand the more generic version?
The problem, as you describe, is that the entityKey function in the first example is opaque since it is of type Func rather than Expression. However, you can get the behavior you want by implementing a Compose() method to combine two expressions. I posted the code to implement compose in this question: use Expression<Func<T,X>> in Linq contains extension.
With Compose() implemented, your function can be implemented as below:
public static List<T> Load<T>(this IQueryable<T> entityQuery,
int[] entityIds,
// note that this is an expression now
Expression<Func<T, int>> entityKey,
int batchSize = 500,
Expression<Func<T, bool>> postFilter = null)
where T : EntityObject
{
Expression<Func<int, bool>> containsExpression = id => entityIds.Contains(id);
Expression<Func<T, bool>> whereInEntityIdsExpression = containsExpression.Compose(entityKey);
IQueryable<T> filteredById = entityQuery.Where(whereInEntityIdsExpression);
// if your post filter is compilable to SQL, you might as well do the filtering
// in the database
if (postFilter != null) { filteredById = filteredById.Where(postFilter); }
// finally, pull into memory
return filteredById.ToList();
}
I am trying to get the following method to work:
private static IQueryable<TObject> ApplyOrderBy<TObject, TKey>(IQueryable<TObject> query, OrderByDirection orderByDirection,
Expression<Func<TObject, TKey>> sortExpression, ref bool first)
{
if (orderByDirection == OrderByDirection.None || sortExpression == null) return;
if (orderByDirection != OrderByDirection.Ascending && orderByDirection != OrderByDirection.Descending)
throw new Exception(string.Format("Should never get here! Unknown OrderByDirection enum - '{0}'.", orderByDirection));
if (first)
{
first = false;
query = orderByDirection == OrderByDirection.Ascending
? query.OrderBy(sortExpression)
: query.OrderByDescending(sortExpression);
}
else
{
query = orderByDirection == OrderByDirection.Ascending
? ((IOrderedQueryable<TObject>)query).ThenBy(sortExpression)
: ((IOrderedQueryable<TObject>)query).ThenByDescending(sortExpression);
}
return query;
}
This method works great if you call it like this:
ApplyOrderByToGet(ref query, OrderByDirection.Ascending, x => x.StartDateTime, ref first);
The sort expression then has a strongly typed DateTime as the type and LINQ to SQL is happy. However, if you want to pass an array of these expressions with varying types, you ultimately need a list with "object" as the type. Problem is that LINQ to SQL does not figure out that the type is not object, but instead is DateTime. This works with a regular list using LINQ to objects.
Seeing as it’s possible to navigate the expression tree and find out what the type is, would it be possible to cast/convert the expression before calling ApplyOrderBy?
Cast or convert from:
Expression<Func<T, object>>
to:
Expression<Func<T, DateTime>>