/// <summary> /// Creates a <see cref="IProjection" /> for the given <see cref="Expression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The context for the projection. /// </param> /// <returns> /// The resolved <see cref="IProjection" /> instance. /// </returns> /// <exception cref="NotSupportedException"> /// The <see cref="Expression" /> could not be resolved as it may contain unsupported features or similar. /// </exception> public static IProjection GetProjection ( Expression expression, HelperContext context ) { IEnumerable <IExpressionHandler> handlers = FlowQueryHelper .GetExpressionHandlers(expression.NodeType) .Where(x => x.CanHandleProjectionOf(expression, context)) .ToArray(); if (handlers.Any()) { return(GetProjectionUsing(handlers, expression, context)); } if (expression.NodeType == ExpressionType.Parameter && expression.ToString() == context.RootAlias) { throw new NotSupportedException ( "Unable to select the root entity like 'x => x', select without an expression instead" ); } return(GetValueProjection(expression)); }
/// <summary> /// Creates a <see cref="ICriterion" /> for the given <see cref="Expression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> public static ICriterion GetCriterion(Expression expression, HelperContext context) { switch (expression.NodeType) { case ExpressionType.AndAlso: case ExpressionType.OrElse: case ExpressionType.ExclusiveOr: case ExpressionType.Equal: case ExpressionType.NotEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: return(GetCriterionForBinary(expression as BinaryExpression, context)); case ExpressionType.Call: return(GetCriterionForMethodCall(expression as MethodCallExpression, context)); case ExpressionType.Lambda: return(GetCriterion(((LambdaExpression)expression).Body, context)); case ExpressionType.Not: return(Restrictions.Not(GetCriterion(((UnaryExpression)expression).Operand, context))); case ExpressionType.MemberAccess: return(Restrictions.Eq(ExpressionHelper.GetPropertyName(expression, context.RootAlias), true)); case ExpressionType.Invoke: return(GetCriterionForInvoke(expression as InvocationExpression, context)); default: return(Restrictions.Eq(Projections.Constant(ExpressionHelper.GetValue <bool>(expression)), true)); } }
/// <summary> /// Creates a <see cref="ICriterion" /> for a binary operation. /// </summary> /// <param name="a"> /// Expression A. /// </param> /// <param name="b"> /// Expression B. /// </param> /// <param name="type"> /// The operation type. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> public static ICriterion GetCriterionForBinary ( Expression a, Expression b, ExpressionType type, HelperContext context ) { if (a.NodeType == ExpressionType.Convert) { return(GetCriterionForBinary(((UnaryExpression)a).Operand, b, type, context)); } if (b.NodeType == ExpressionType.Convert) { return(GetCriterionForBinary(a, ((UnaryExpression)b).Operand, type, context)); } switch (type) { case ExpressionType.AndAlso: return(Restrictions.And(GetCriterion(a, context), GetCriterion(b, context))); case ExpressionType.OrElse: return(Restrictions.Or(GetCriterion(a, context), GetCriterion(b, context))); case ExpressionType.ExclusiveOr: ICriterion criterionA = GetCriterion(a, context); ICriterion criterionB = GetCriterion(b, context); return(Restrictions .Or ( Restrictions.And(criterionA, Restrictions.Not(criterionB)), Restrictions.And(criterionB, Restrictions.Not(criterionA)) )); } bool isProjectedA = IsProjected(a, context); bool isProjectedB = IsProjected(b, context); // Projection-Projection if (isProjectedA && isProjectedB) { return(GetProjectionProjectionCriterion ( ProjectionHelper.GetProjection(a, context), ProjectionHelper.GetProjection(b, context), type )); } if (isProjectedA || !isProjectedB) { return(GetProjectionValueCriterion(a, ExpressionHelper.GetValue(b), type, context, false)); } return(GetProjectionValueCriterion(b, ExpressionHelper.GetValue(a), type, context, true)); }
/// <summary> /// Determines whether the given <see cref="Expression" /> is projected. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// True if the <see cref="Expression" /> is considered to be projected; false otherwise. /// </returns> public static bool IsProjected(Expression expression, HelperContext context) { string expressionRoot = ExpressionHelper.GetRoot(expression); return(expression is BinaryExpression || (expressionRoot != null && (expressionRoot == context.RootAlias || context.Data.Aliases.ContainsValue(expressionRoot)))); }
/// <summary> /// Creates a <see cref="IProjection" /> from the given <see cref="Expression" />. /// </summary> /// <param name="handlers"> /// The set of <see cref="IExpressionHandler" /> instances to use when resolving the /// <see cref="IProjection" /> of the given <see cref="Expression" />. /// </param> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The context for the projection. /// </param> /// <returns> /// The created <see cref="IProjection" />. /// </returns> /// <exception cref="NotSupportedException"> /// The <see cref="Expression" /> could not be resolved as it may contain unsupported features or similar. /// </exception> private static IProjection GetProjectionUsing ( IEnumerable <IExpressionHandler> handlers, Expression expression, HelperContext context ) { foreach (IExpressionHandler handler in handlers) { IProjection projection = handler.Project(expression, context); if (projection != null) { return(projection); } } throw new NotSupportedException(string.Format(NotSupportedMessage, expression)); }
/// <summary> /// Creates a <see cref="ICriterion" /> for the given <see cref="InvocationExpression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> public static ICriterion GetCriterionForInvoke ( InvocationExpression expression, HelperContext context ) { if (expression.Expression.Type == typeof(WhereDelegate)) { string property = ExpressionHelper.GetPropertyName(expression.Arguments[0], context.RootAlias); var isExpression = ExpressionHelper.GetValue <IsExpression>(expression.Arguments[1]); ICriterion criterion = isExpression.Compile(property); return(criterion); } return(Restrictions.Eq(Projections.Constant(ExpressionHelper.GetValue <bool>(expression)), true)); }
/// <summary> /// Gets the property name for the given <see cref="Expression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="expectedRoot"> /// The expected root. /// </param> /// <param name="isRooted"> /// Indicates whether the property is rooted on an alias or not. /// </param> /// <param name="context"> /// The <see cref="HelperContext" /> context, used only (and required) when <paramref name="isRooted" /> is /// set to true. /// </param> /// <returns> /// The property name. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="expression" /> is null. /// </exception> public static string GetPropertyName ( Expression expression, string expectedRoot, bool isRooted = false, HelperContext context = null ) { if (expression == null) { throw new ArgumentNullException("expression"); } string property = GetPropertyName(expression); string[] splits = property.Split('.'); if (splits[0] == expectedRoot) { var parts = new string[splits.Length - 1]; for (int i = 1; i < splits.Length; i++) { parts[i - 1] = splits[i]; } return(string.Join(".", parts)); } if (isRooted && context != null && splits.Length >= 2) { string root = string.Join("_", splits.Take(splits.Length - 1)); // only modify |property| if there is in fact an alias matching |root|. if (context.Data.Aliases.ContainsValue(root)) { property = root + "." + splits.Last(); } } return(property); }
/// <summary> /// Creates a <see cref="ICriterion" /> for the given <see cref="BinaryExpression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> public static ICriterion GetCriterionForBinary(BinaryExpression expression, HelperContext context) { return(GetCriterionForBinary(expression.Left, expression.Right, expression.NodeType, context)); }
/// <summary> /// Creates <see cref="ICriterion" /> for a comparison between a value and a projection. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="value"> /// The value. /// </param> /// <param name="type"> /// The comparison type. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <param name="overTurned"> /// Indicates whether the comparison has been reversed to simplify other code. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> /// <exception cref="NotSupportedException"> /// The <see cref="Expression" /> could not be resolved as it may contain unsupported features or similar. /// </exception> public static ICriterion GetProjectionValueCriterion ( Expression expression, object value, ExpressionType type, HelperContext context, bool overTurned ) { if (overTurned) { switch (type) { case ExpressionType.GreaterThan: type = ExpressionType.LessThan; break; case ExpressionType.GreaterThanOrEqual: type = ExpressionType.LessThanOrEqual; break; case ExpressionType.LessThan: type = ExpressionType.GreaterThan; break; case ExpressionType.LessThanOrEqual: type = ExpressionType.GreaterThanOrEqual; break; } } IProjection projection = ProjectionHelper.GetProjection(expression, context); switch (type) { case ExpressionType.Equal: if (value == null) { return(Restrictions.IsNull(projection)); } if (value is bool) { return(GetCriterion((bool)value ? expression : Expression.Not(expression), context)); } return(Restrictions.Eq(projection, value)); case ExpressionType.NotEqual: if (value == null) { return(Restrictions.IsNotNull(projection)); } if (value is bool) { return(GetCriterion(!(bool)value ? expression : Expression.Not(expression), context)); } return(Restrictions.Not(Restrictions.Eq(projection, value))); case ExpressionType.GreaterThan: return(Restrictions.Gt(projection, value)); case ExpressionType.GreaterThanOrEqual: return(Restrictions.Ge(projection, value)); case ExpressionType.LessThan: return(Restrictions.Lt(projection, value)); case ExpressionType.LessThanOrEqual: return(Restrictions.Le(projection, value)); default: throw new NotSupportedException ( "the expression contains unsupported features, please revise your code" ); } }
/// <summary> /// Creates a <see cref="ICriterion" /> for the given <see cref="MethodCallExpression" />. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="context"> /// The helper context. /// </param> /// <returns> /// The created <see cref="ICriterion" />. /// </returns> public static ICriterion GetCriterionForMethodCall ( MethodCallExpression expression, HelperContext context ) { if (IsProjected(expression, context)) { // Not a value expression IProjection projection = ProjectionHelper .GetProjection ( expression.Object ?? expression.Arguments[0], context ); int i = expression.Object == null ? 1 : 0; switch (expression.Method.Name) { case "In": case "IsIn": object value = ExpressionHelper.GetValue(expression.Arguments[i]); if (!(value is ICollection)) { if (value is IEnumerable) { var objs = new List <object>(); foreach (object obj in value as IEnumerable) { objs.Add(obj); } return(Restrictions .In ( projection, objs.ToArray() )); } if (value is IDetachedImmutableFlowQuery) { return(Subqueries .PropertyIn ( ExpressionHelper.GetPropertyName(expression, context.RootAlias), (value as IDetachedImmutableFlowQuery).Criteria )); } } return(Restrictions .In ( projection, value as ICollection )); case "Between": case "IsBetween": return(Restrictions .Between ( projection, ExpressionHelper.GetValue(expression.Arguments[i]), ExpressionHelper.GetValue(expression.Arguments[i + 1]) )); case "Like": case "IsLike": return(GetLikeCriterion(projection, expression.Arguments[i], MatchMode.Exact)); case "StartsWith": return(GetLikeCriterion(projection, expression.Arguments[i], MatchMode.Start)); case "EndsWith": return(GetLikeCriterion(projection, expression.Arguments[i], MatchMode.End)); case "Contains": return(GetLikeCriterion(projection, expression.Arguments[i], MatchMode.Anywhere)); case "IsLessThan": return(Restrictions.Lt(projection, ExpressionHelper.GetValue(expression.Arguments[i]))); case "IsLessThanOrEqualTo": return(Restrictions.Le(projection, ExpressionHelper.GetValue(expression.Arguments[i]))); case "IsGreaterThan": return(Restrictions.Gt(projection, ExpressionHelper.GetValue(expression.Arguments[i]))); case "IsGreaterThanOrEqualTo": return(Restrictions.Ge(projection, ExpressionHelper.GetValue(expression.Arguments[i]))); case "IsEqualTo": return(Restrictions.Eq(projection, ExpressionHelper.GetValue(expression.Arguments[i]))); case "IsNull": return(Restrictions.IsNull(projection)); case "IsNotNull": return(Restrictions.IsNotNull(projection)); } throw new NotSupportedException ( "The expression contains unsupported features, please revise your code" ); } return(Restrictions.Eq(Projections.Constant(ExpressionHelper.GetValue <bool>(expression)), true)); }