I am trying to create a dynamic where clause to use with the Entity Framework. This is the article that I am following:
http://www.codeproject.com/Articles/493917/Dynamic-Querying-with-LINQ-to-Entities-and-Express
The Dynamic Where section in the article is where the explanation and code is.
I created a class with one property to try it out:
public class CVLocalFilteringInputDTO
{
[DbFieldMapAttribute("PersonGenderCode")]
public Nullable<bool> PersonGenderCode { get; set; }
}
The Entity Framework class that I am using has a similar property:
public partial class SearchPSSCitizen {
... constructor here ...
public Nullable<bool> PersonGenderCode { get; set; }
... some additional properties here ...
}
In the top of the code there is a method for comparing booleans:
private static readonly MethodInfo BooleanEqualsMethod =
typeof (bool).GetMethod(@"Equals", BindingFlags.Instance |
BindingFlags.Public, null, new[] {typeof (bool)}, null);
And the method that uses this function looks like this:
private static Expression<Func<TDbType, bool>> ApplyBoolCriterion<TDbType,
TSearchCriteria>(TSearchCriteria searchCriteria, PropertyInfo searchCriterionPropertyInfo,
Type dbType, MemberInfo dbFieldMemberInfo, Expression<Func<TDbType, bool>> predicate)
{
// Check if a search criterion was provided
var searchBool = searchCriterionPropertyInfo.GetValue(searchCriteria) as bool?;
if (searchBool == null)
{
return predicate;
}
// Then "and" it to the predicate.
// e.g. predicate = predicate.And(x => x.isActive.Contains(searchCriterion.IsActive)); ...
// Create an "x" as TDbType
var dbTypeParameter = Expression.Parameter(dbType, @"x");
// Get at x.isActive
var dbFieldMember = Expression.MakeMemberAccess(dbTypeParameter, dbFieldMemberInfo);
// Create the criterion as a constant
var criterionConstant = new Expression[] {Expression.Constant(searchBool)};
// Create the MethodCallExpression like x.isActive.Equals(criterion)
var equalsCall = Expression.Call(dbFieldMember, BooleanEqualsMethod, criterionConstant);
// Create a lambda like x => x.isActive.Equals(criterion)
var lambda = Expression.Lambda(equalsCall, dbTypeParameter) as Expression<Func<TDbType, bool>>;
// Apply!
return predicate.And(lambda);
}
Since i am not using the primitive type bool
but Nullable<bool>
, i changed the BooleanEqualsMethod to:
private static readonly MethodInfo BooleanEqualsMethod =
typeof(Nullable<bool>).GetMethod(@"Equals", BindingFlags.Instance |
BindingFlags.Public, null, new[] { typeof(object) }, null);
and
var searchBool = searchCriterionPropertyInfo.GetValue(searchCriteria) as bool?;
to
var searchBool = searchCriterionPropertyInfo.GetValue(searchCriteria) as Nullable<bool>;
Notice that the Equals method of Nullable takes an object
type as argument rather than a bool
type.
When I run the code, the following error occours:
Additional information: Expression of type 'System.Boolean' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)'
From Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)' I learned that the expression should be converted to an object type and passed to the Expression.Call method:
var converted = Expression.Convert(criterionConstant.First(), typeof(object));
var equalsCall = Expression.Call(dbFieldMember, BooleanEqualsMethod, converted);
This does not throw any exceptions and the lambda expression becomes:
{x => x.PersonGenderCode.Equals(Convert(False))}
However, when I try to query the database with the predicate:
DBEntities db = new DBEntities();
var query = db.SearchPSSCitizen.AsExpandable().Where(predicate) as IQueryable<SearchPSSCitizen>;
Console.WriteLine(query.Count());
I get the following exception:
Additional information: Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
When hovering the predicate in the Where clause, it looks like this:
{f => (True AndAlso Invoke(x => x.PersonGenderCode.Equals(Convert(False)), f))}
When searching for the exception above, the answer is to change "Equals" to "==", however, I am not sure how to do that in this case.
What should I do to move on and fix this issue?