using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace DevExpress.DevAV.Common.Utils {
///
/// Provides methods to perform operations with lambda expression trees.
///
public class ExpressionHelper {
class ValueHolder {
public readonly T value;
public ValueHolder(T value) {
this.value = value;
}
}
static readonly Dictionary TraitsCache = new Dictionary();
///
/// Builds a lambda expression that compares an entity property value with a given constant value.
///
/// An owner type of the property.
/// A primary key property type.
/// A lambda expression that returns the property value for a given entity.
/// A constant value to compare with entity property value.
public static Expression> GetValueEqualsExpression(Expression> getPropertyExpression, TProperty constant) {
Expression equalExpression = Expression.Equal(getPropertyExpression.Body, Expression.Convert(Expression.Field(Expression.Constant(new ValueHolder(constant)), "value"), getPropertyExpression.Body.Type));
return Expression.Lambda>(equalExpression, getPropertyExpression.Parameters.Single());
}
///
/// Returns an instance of the EntityTraits class that encapsulates operations to obtain and set the primary key value of a given entity.
///
/// A type used as a key to cache compiled lambda expressions.
/// An owner type of the primary key property.
/// A primary key property type.
/// An instance of the TOwner type which type is used as a key to cache compiled lambda expressions.
/// A lambda expression that returns the primary key value for a given entity.
public static EntityTraits GetEntityTraits(TOwner owner, Expression> getPropertyExpression) {
object traits = null;
if(!TraitsCache.TryGetValue(owner.GetType(), out traits)) {
traits = new EntityTraits(getPropertyExpression.Compile(), GetSetValueActionExpression(getPropertyExpression).Compile(), GetHasValueFunctionExpression(getPropertyExpression).Compile());
TraitsCache[owner.GetType()] = traits;
}
return (EntityTraits)traits;
}
///
/// Determines whether the given entity satisfies the condition represented by a lambda expression.
///
/// A type of the given object.
/// An object to test.
/// A function that determines whether the given object satisfies the condition.
public static bool IsFitEntity(TEntity entity, Expression> predicate) where TEntity : class {
return predicate == null || (new TEntity[] { entity }.AsQueryable().Any(predicate));
}
///
/// Converts a property reference represented as a lambda expression to a property name.
///
/// A lambda expression that returns the property value.
public static string GetPropertyName(LambdaExpression expression) {
Expression body = expression.Body;
if(body is UnaryExpression) {
body = ((UnaryExpression)body).Operand;
}
var memberExpression = ValidateMemberExpression((MemberExpression)body);
return memberExpression.Member.Name;
}
static MemberExpression ValidateMemberExpression(MemberExpression memberExpression) {
if(IsNullableValueExpression(memberExpression))
memberExpression = (MemberExpression)memberExpression.Expression;
return memberExpression;
}
static bool IsNullableValueExpression(MemberExpression memberExpression) {
var propertyInfo = (PropertyInfo)memberExpression.Member;
return propertyInfo.PropertyType.IsValueType && propertyInfo.ReflectedType == typeof(Nullable<>).MakeGenericType(propertyInfo.PropertyType) && propertyInfo.Name == "Value";
}
static Expression> GetSetValueActionExpression(Expression> getPropertyExpression) {
MemberExpression body = ValidateMemberExpression((MemberExpression)getPropertyExpression.Body);
ParameterExpression thisParameter = getPropertyExpression.Parameters.Single();
ParameterExpression propertyValueParameter = Expression.Parameter(typeof(TProperty), "propertyValue");
Expression keyValueExpression = propertyValueParameter;
if(IsNullableValueExpression((MemberExpression)getPropertyExpression.Body)) {
var constructor = typeof(Nullable<>).MakeGenericType(typeof(TProperty)).GetConstructor(new Type[] { typeof(TProperty) });
keyValueExpression = Expression.New(constructor, keyValueExpression);
}
BinaryExpression assignPropertyValueExpression = Expression.Assign(body, keyValueExpression);
return Expression.Lambda>(assignPropertyValueExpression, thisParameter, propertyValueParameter);
}
static Expression> GetHasValueFunctionExpression(Expression> getPropertyExpression) {
MemberExpression memberExpression = (MemberExpression)getPropertyExpression.Body;
if(IsNullableValueExpression(memberExpression)) {
Expression equalExpression = Expression.NotEqual(memberExpression.Expression, Expression.Constant(null));
return Expression.Lambda>(equalExpression, getPropertyExpression.Parameters.Single());
}
return x => true;
}
}
///
/// Incapsulates operations to obtain and set the primary key value of a given entity.
///
/// An owner type of the primary key property.
/// A primary key property type.
public class EntityTraits {
///
/// Initializes a new instance of EntityTraits class.
///
/// A function that returns the primary key value of a given entity.
/// An action that assigns the primary key value to a given entity.
/// A function that determines whether given the entity has a primary key assigned.
public EntityTraits(Func getPrimaryKeyFunction, Action setPrimaryKeyAction, Func hasPrimaryKeyFunction) {
this.GetPrimaryKey = getPrimaryKeyFunction;
this.SetPrimaryKey = setPrimaryKeyAction;
this.HasPrimaryKey = hasPrimaryKeyFunction;
}
///
/// The function that returns the primary key value of a given entity.
///
public Func GetPrimaryKey { get; private set; }
///
/// The action that assigns the primary key value to a given entity.
///
public Action SetPrimaryKey { get; private set; }
///
/// A function that determines whether the given entity has a primary key assigned (the primary key is not null). Always returns true if the primary key is a non-nullable value type.
///
///
public Func HasPrimaryKey { get; private set; }
}
}