Represents an order by clause.
        private void TranslateThenBy(MethodCallExpression methodCallExpression)
        {
            if (methodCallExpression.Arguments.Count != 2)
            {
                throw new ArgumentOutOfRangeException("methodCallExpression");
            }

            if (_orderBy == null)
            {
                throw new NotSupportedException("ThenBy or ThenByDescending can only be used after OrderBy or OrderByDescending.");
            }

            var key = (LambdaExpression)StripQuote(methodCallExpression.Arguments[1]);
            var direction = (methodCallExpression.Method.Name == "ThenByDescending") ? OrderByDirection.Descending : OrderByDirection.Ascending;
            var clause = new OrderByClause(key, direction);

            _orderBy.Add(clause);
        }
        private void TranslateOrderBy(MethodCallExpression methodCallExpression)
        {
            if (methodCallExpression.Arguments.Count != 2)
            {
                throw new ArgumentOutOfRangeException("methodCallExpression");
            }

            if (_orderBy != null)
            {
                throw new NotSupportedException("Only one OrderBy or OrderByDescending clause is allowed (use ThenBy or ThenByDescending for multiple order by clauses).");
            }

            var key = (LambdaExpression)StripQuote(methodCallExpression.Arguments[1]);
            var direction = (methodCallExpression.Method.Name == "OrderByDescending") ? OrderByDirection.Descending : OrderByDirection.Ascending;
            var clause = new OrderByClause(key, direction);

            _orderBy = new List<OrderByClause>();
            _orderBy.Add(clause);
        }
        private void TranslateMaxMin(MethodCallExpression methodCallExpression)
        {
            var methodName = methodCallExpression.Method.Name;

            if (_orderBy != null)
            {
                var message = string.Format("{0} cannot be used with OrderBy.", methodName);
                throw new NotSupportedException(message);
            }
            if (_skip != null || _take != null)
            {
                var message = string.Format("{0} cannot be used with Skip or Take.", methodName);
                throw new NotSupportedException(message);
            }

            switch (methodCallExpression.Arguments.Count)
            {
                case 1:
                    break;
                case 2:
                    if (_projection != null)
                    {
                        var message = string.Format("{0} must be used with either Select or a selector argument, but not both.", methodName);
                        throw new NotSupportedException(message);
                    }
                    _projection = (LambdaExpression)StripQuote(methodCallExpression.Arguments[1]);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("methodCallExpression");
            }
            if (_projection == null)
            {
                var message = string.Format("{0} must be used with either Select or a selector argument.", methodName);
                throw new NotSupportedException(message);
            }

            // implement Max/Min by sorting on the relevant field(s) and taking the first result
            _orderBy = new List<OrderByClause>();
            if (_projection.Body.NodeType == ExpressionType.New)
            {
                // take the individual constructor arguments and make new lambdas out of them for the OrderByClauses
                var newExpression = (NewExpression)_projection.Body;
                foreach (var keyExpression in newExpression.Arguments)
                {
                    var delegateTypeGenericDefinition = typeof(Func<,>);
                    var delegateType = delegateTypeGenericDefinition.MakeGenericType(_projection.Parameters[0].Type, keyExpression.Type);
                    var keyLambda = Expression.Lambda(delegateType, keyExpression, _projection.Parameters);
                    var clause = new OrderByClause(keyLambda, (methodName == "Min") ? OrderByDirection.Ascending : OrderByDirection.Descending);
                    _orderBy.Add(clause);
                }
            }
            else
            {
                var clause = new OrderByClause(_projection, (methodName == "Min") ? OrderByDirection.Ascending : OrderByDirection.Descending);
                _orderBy.Add(clause);
            }

            _take = Expression.Constant(1);
            SetElementSelector(methodCallExpression, source => source.Cast<object>().First());
        }
        private void TranslateLast(MethodCallExpression methodCallExpression)
        {
            LambdaExpression predicate = null;
            switch (methodCallExpression.Arguments.Count)
            {
                case 1:
                    break;
                case 2:
                    predicate = (LambdaExpression)StripQuote(methodCallExpression.Arguments[1]);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("methodCallExpression");
            }
            CombinePredicateWithWhereClause(methodCallExpression, predicate);

            // when using OrderBy without Take Last can be much faster by reversing the sort order and using First instead of Last
            if (_orderBy != null && _take == null)
            {
                for (int i = 0; i < _orderBy.Count; i++)
                {
                    var clause = _orderBy[i];
                    var oppositeDirection = (clause.Direction == OrderByDirection.Descending) ? OrderByDirection.Ascending : OrderByDirection.Descending;
                    _orderBy[i] = new OrderByClause(clause.Key, oppositeDirection);
                }
                _take = Expression.Constant(1);

                switch (methodCallExpression.Method.Name)
                {
                    case "Last":
                        SetElementSelector(methodCallExpression, source => source.Cast<object>().First());
                        break;
                    case "LastOrDefault":
                        SetElementSelector(methodCallExpression, source => source.Cast<object>().FirstOrDefault());
                        break;
                }
            }
            else
            {
                switch (methodCallExpression.Method.Name)
                {
                    case "Last":
                        SetElementSelector(methodCallExpression, source => source.Cast<object>().Last());
                        break;
                    case "LastOrDefault":
                        SetElementSelector(methodCallExpression, source => source.Cast<object>().LastOrDefault());
                        break;
                }
            }
        }
Example #5
0
        private void TranslateMaxMin(MethodCallExpression methodCallExpression)
        {
            var methodName = methodCallExpression.Method.Name;

            if (_orderBy != null)
            {
                var message = string.Format("{0} cannot be used with OrderBy.", methodName);
                throw new NotSupportedException(message);
            }
            if (_skip != null || _take != null)
            {
                var message = string.Format("{0} cannot be used with Skip or Take.", methodName);
                throw new NotSupportedException(message);
            }

            switch (methodCallExpression.Arguments.Count)
            {
            case 1:
                break;

            case 2:
                if (_projection != null)
                {
                    var message = string.Format("{0} must be used with either Select or a selector argument, but not both.", methodName);
                    throw new NotSupportedException(message);
                }
                _projection = (LambdaExpression)StripQuote(methodCallExpression.Arguments[1]);
                break;

            default:
                throw new ArgumentOutOfRangeException("methodCallExpression");
            }
            if (_projection == null)
            {
                var message = string.Format("{0} must be used with either Select or a selector argument.", methodName);
                throw new NotSupportedException(message);
            }

            // implement Max/Min by sorting on the relevant field(s) and taking the first result
            _orderBy = new List <OrderByClause>();
            if (_projection.Body.NodeType == ExpressionType.New)
            {
                // take the individual constructor arguments and make new lambdas out of them for the OrderByClauses
                var newExpression = (NewExpression)_projection.Body;
                foreach (var keyExpression in newExpression.Arguments)
                {
                    var delegateTypeGenericDefinition = typeof(Func <,>);
                    var delegateType = delegateTypeGenericDefinition.MakeGenericType(_projection.Parameters[0].Type, keyExpression.Type);
                    var keyLambda    = Expression.Lambda(delegateType, keyExpression, _projection.Parameters);
                    var clause       = new OrderByClause(keyLambda, (methodName == "Min") ? OrderByDirection.Ascending : OrderByDirection.Descending);
                    _orderBy.Add(clause);
                }
            }
            else
            {
                var clause = new OrderByClause(_projection, (methodName == "Min") ? OrderByDirection.Ascending : OrderByDirection.Descending);
                _orderBy.Add(clause);
            }

            _take = 1;
            SetElementSelector(methodCallExpression, source => source.Cast <object>().First());
        }
        private void TranslateLast(MethodCallExpression methodCallExpression)
        {
            if (methodCallExpression.Arguments.Count != 1)
            {
                throw new ArgumentOutOfRangeException("methodCallExpression");
            }

            if (_elementSelector != null)
            {
                var message = string.Format("{0} cannot be combined with any other element selector.", methodCallExpression.Method.Name);
                throw new InvalidOperationException(message);
            }

            // when using OrderBy without Take Last can be much faster by reversing the sort order and using First instead of Last
            if (_orderBy != null && _take == null)
            {
                for (int i = 0; i < _orderBy.Count; i++)
                {
                    var clause = _orderBy[i];
                    var oppositeDirection = (clause.Direction == OrderByDirection.Descending) ? OrderByDirection.Ascending : OrderByDirection.Descending;
                    _orderBy[i] = new OrderByClause(clause.Key, oppositeDirection);
                }
                _take = Expression.Constant(1);

                switch (methodCallExpression.Method.Name)
                {
                    case "Last":
                        _elementSelector = (IEnumerable source) => source.Cast<object>().First();
                        break;
                    case "LastOrDefault":
                        _elementSelector = (IEnumerable source) => source.Cast<object>().FirstOrDefault();
                        break;
                }
            }
            else
            {
                switch (methodCallExpression.Method.Name)
                {
                    case "Last":
                        _elementSelector = (IEnumerable source) => source.Cast<object>().Last();
                        break;
                    case "LastOrDefault":
                        _elementSelector = (IEnumerable source) => source.Cast<object>().LastOrDefault();
                        break;
                }
            }
        }