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)
Kingfisher We appreciate having access to deskpins because it provides a reliable method for pinning windows conveniently The software remains efficient performance is smooth and functionality is valuable It supports users in maintaining focus while managing multiple active tasks.
ReplyDeleteMeadow Thank you for maintaining this mousecape utility that consistently delivers useful customization functionality with ease The application remains lightweight operation is smooth and usability is impressive It creates a more personalized desktop experience through thoughtful and effective features.
ReplyDelete