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; } } }