using System; using System.Linq.Expressions; namespace DataAccess.DomainModel.Entities.BaseEntities { public abstract class BaseSearchClass<T> { #region Variables private SearchClauseSeparator _clauseSeparator = SearchClauseSeparator.Or; #endregion #region Properties public SearchClauseSeparator ClauseSeparator { get { return _clauseSeparator; } set { _clauseSeparator = value; } } #endregion #region Expression Building Methods public virtual Expression<Func<T, bool>> BuildExpression() { // Create return object var param = Expression.Parameter(typeof(T), "x"); Expression workingExpression = null; // Loop through all properties foreach (var property in this.GetType().GetProperties()) { // Skip if missing SearchProperty attribute var attrs = property.GetCustomAttributes(typeof(SearchableProperty), false); if (attrs == null || attrs.Length == 0) continue; // Get the attribute properties var attr = attrs[0] as SearchableProperty; var searchOperation = attr.SearchOperation; var dataFieldName = attr.DataFieldName; if (string.IsNullOrEmpty(dataFieldName)) dataFieldName = property.Name; var prependOperator = attr.PrependOperator; var expressionBuildingDelegate = attr.ExpressionBuildingDelegate; // Get the value var value = property.GetValue(this, null); if (value == null) continue; // Build the expression var exp = GenerateExpressionForProperty(param, searchOperation, dataFieldName, value, expressionBuildingDelegate); if (exp == null) continue; // Combine expressions accordingly if ((_clauseSeparator == SearchClauseSeparator.Or && prependOperator == SearchClauseSeparator.None) || prependOperator == SearchClauseSeparator.Or) { workingExpression = OrCombineExpressions(workingExpression, exp); } else { workingExpression = AndCombineExpressions(workingExpression, exp); } } // See if expression is null if (workingExpression == null) { workingExpression = CreateDefaultExpression(param); } // Return the lambda of the expression return Expression.Lambda<Func<T, bool>>(workingExpression, param); } private Expression GenerateExpressionForProperty(Expression parameter, SearchOperation searchOperation, string propertyName, object value, ExpressionDelegate expressionDelegate) { // Stop if value is null if (value == null) return null; // See if field is nullable var propertyType = typeof(T).GetProperty(propertyName).PropertyType; var isNullable = propertyType.Name.StartsWith("Nullable"); // See which operation to perform var operation = expressionDelegate; if (expressionDelegate == null) operation = DetermineOperator(searchOperation); // See if value is an array if (value.GetType().IsArray) { Expression returnExpression = null; var theArray = value as Array; foreach (var item in theArray) { returnExpression = OrCombineExpressions(returnExpression, BuildSingleExpression(parameter, propertyName, propertyType, item, operation, isNullable)); } return returnExpression; } // See if value is a DualValuedObject (for performing BETWEEN operations) if (value is DualValuedObject) { // Stop if either value is null var dvo = value as DualValuedObject; if (dvo.Value1 == null || dvo.Value2 == null) return null; // Build left side var left = BuildSingleExpression(parameter, propertyName, propertyType, dvo.Value1, Expression.GreaterThanOrEqual, isNullable); // Build right side var right = BuildSingleExpression(parameter, propertyName, propertyType, dvo.Value2, Expression.LessThanOrEqual, isNullable); // Combine them using AND return AndCombineExpressions(left, right); } // Return a single expression return BuildSingleExpression(parameter, propertyName, propertyType, value, operation, isNullable); } private Expression BuildSingleExpression(Expression parameter, string propertyName, Type propertyType, object value, ExpressionDelegate delegateFunction, bool isNullable) { // Throw exception if property and value or not of the same type if (!propertyType.ToString().Contains(value.GetType().ToString())) { throw new Exception(propertyName + " is a " + propertyType.ToString() + ", invalid attempt to compare to a " + value.GetType().ToString()); } // Create the left and right hands of the expression var left = Expression.Property(parameter, propertyName); if (isNullable) left = Expression.Property(left, "Value"); var right = Expression.Constant(value); // Combine using the specified operation return delegateFunction(left, right); } private Expression OrCombineExpressions(Expression left, Expression right) { return (left == null) ? right : Expression.Or(left, right); } private Expression AndCombineExpressions(Expression left, Expression right) { return (left == null) ? right : Expression.And(left, right); } private Expression CreateDefaultExpression(Expression parameter) { return BuildSingleExpression(parameter, "ID", typeof(int), 0, Expression.GreaterThan, false); } #endregion #region SearchOperation Methods private ExpressionDelegate DetermineOperator(SearchOperation type) { switch (type) { case SearchOperation.GreaterThanOrEqualTo : return Expression.GreaterThanOrEqual; case SearchOperation.GreaterThan : return Expression.GreaterThan; case SearchOperation.LessThanOrEqualTo : return Expression.LessThanOrEqual; case SearchOperation.LessThan : return Expression.LessThan; case SearchOperation.Contains : return Contains; case SearchOperation.StartsWith : return StartsWith; case SearchOperation.EndsWith : return EndsWith; case SearchOperation.Equal : default: return Expression.Equal; } } private static Expression CustomStringEvaluator(string stringEvaluator, Expression property, ConstantExpression value) { var method = typeof(string).GetMethod(stringEvaluator, new[] { typeof(string) }); return Expression.Call(property, method, value); } private static Expression Contains(Expression property, ConstantExpression value) { return CustomStringEvaluator("Contains", property, value); } private static Expression StartsWith(Expression property, ConstantExpression value) { return CustomStringEvaluator("StartsWith", property, value); } private static Expression EndsWith(Expression property, ConstantExpression value) { return CustomStringEvaluator("EndsWith", property, value); } #endregion } }
Tuesday, October 4, 2011
Dynamic expression for LINQ
Here is a class I wrote a while ago for building dynamic lambda expression to be used against a LINQ to SQL data context. Yes, it's a giant beast that shows I didn't have any refactoring skills at some point in time, but the intent of the class is good and will hopefully help out someone.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment