I am trying to dynamically build an expression tree in C#, which is compiled and used as the predicate for LINQ-to-SQL Where() call. The problem is that I am trying to compare an Enum (with int as its underlying type) directly against an Int, but this is failing with the error "The member MyEnumType has no supported translation to SQL".
Code:
ParameterExpression param = Expression.Parameter(typeof(MyClass), "obj"); //input parameter - this exposes a property of the Enum type
MemberExpression enumMember = Expression.Property(param, "MyEnumProperty"); //used to get the Enum typed property from the parameter
//MISSING STEP TO CAST THE ENUM OF THE MEMBER EXPRESSION TO AN INT?
BinaryExpression binaryExpr = Expression.Equal(enumMember, Expression.Constant(1));
LambdaExpression<Func<MyClass, bool>> whereClause = Expression.Lambda(binaryExpr, param);
//when whereClause is used to filter LINQ-to-SQL results, the error is thrown
I'm fairly new to expression trees and I can't figure this out. I have tried using
Expression.Convert(enumMember, typeof(int))
as the first part of the BinaryExpression but this doesn't fix it.
Any help is much appreciated.
An Enum value cannot be treated as an int by default because then you would be able to provide any integer and there would be no compile time check to validate that the provided integer does in fact exist as a value in the Enumeration.
Convert int to Enum using Enum.Use the Enum. ToObject() method to convert integers to enum members, as shown below.
Parse<TEnum>(String, Boolean)Converts the string representation of the name or numeric value of one or more enumerated constants specified by TEnum to an equivalent enumerated object. A parameter specifies whether the operation is case-insensitive.
Cast Int To Enum may be of some help. Go with the 2nd option. The 1st one can cause an exception if the integer is out of the defined range in your Enumeration. In current example I compare to 'magic number' but in real application I am getting data from integer field from DB.
Simply, you shouldn't have to, as long as you've told LINQ-to-SQL about the enum (rather than mapping it as an int
and having a separate property in C# that does the translation). For example, the following works fine:
var param = Expression.Parameter(typeof(DomainObject));
var body = Expression.Equal(Expression.Property(param, "SomeProperty"),
Expression.Constant(YourEnumType.SomeEnum));
var predicate = Expression.Lambda<Func<DomainObject, bool>>(body, param);
var count = db.Table.Where(predicate).Count();
The main point is that my SomeProperty
property is mapped in the dbml to the enum. Simply overtype the type name with the enum type (including namespace).
Likewise, you shouldn't be giving it a 1, but rather the typed enum; for example:
Expression.Constant(Enum.ToObject(typeof(YourEnumType), 1))
(if all you know is 1
)
Thanks To Marc Gravell. (Expression Guru !) See Correct answer. I made a change to an Expression Routine to cater for this scenario. Normal properties or Enums. Incase someone finds this helpful
public static Expression<Func<TPoco, bool>> GetEqualsPredicate<TPoco>(string propertyName,
object value)
Type fieldType )
{
var parameterExp = Expression.Parameter(typeof(TPoco), @"t"); //(tpoco t)
var propertyExp = Expression.Property(parameterExp, propertyName);// (tpoco t) => t.Propertyname
var someValue = fieldType.IsEnum // get and eXpressionConstant. Careful Enums must be reduced
? Expression.Constant(Enum.ToObject(fieldType, value)) // Marc Gravell fix
: Expression.Constant(value, fieldType);
var equalsExp = Expression.Equal(propertyExp, someValue); // yes this could 1 unreadble state if embedding someValue determination
return Expression.Lambda<Func<TPoco, bool>>(equalsExp, parameterExp);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With