0

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?

Community
  • 1
  • 1
Kenci
  • 4,794
  • 15
  • 64
  • 108
  • 1
    have you tried using the `Expression.Equal` instead, don't need to create custom equal method. It of course still requires the 2 operands to have exactly matched type. I understand that `searchBool` is `bool?` so the property should also be `bool?`. If that's correct, I believe you can totally use `Expression.Equal`. – King King Oct 12 '15 at 11:03
  • 1
    The Entity Framework decided to use Nullable so I have to stick with that (for my input class - so that the types match). I just tried with Expression.Equal and it works! :) The "as Nullable" returns a Boolean instead of a Nullable, so I still have to use Expression.Convert though. Thank you. – Kenci Oct 12 '15 at 11:13

0 Answers0