/// <summary>
        /// Private helper to parse a sort expression string and create Comparison delegates based on each property.
        /// </summary>
        /// <param name="sortExpression">Sort expression to parse.</param>
        /// <returns>List of Comparison delegates, one for each property.</returns>
        private static List <Comparison <T> > GetFieldComparisons(String sortExpression)
        {
            SimpleTokenizer        parser        = new SimpleTokenizer(sortExpression);
            List <Comparison <T> > comparisons   = new List <Comparison <T> >(4);
            List <String>          propertyParts = new List <string>(4);
            Boolean moreProperties;

            do
            {
                do
                {
                    String property = parser.ReadIdentity();
                    if (property.Length == 0)
                    {
                        throw new ParserException(parser.Position, sortExpression, "Field or property expected");
                    }

                    propertyParts.Add(property);
                }while (parser.AdvanceIfSymbol('.'));

                moreProperties = parser.AdvanceIfSymbol(',');

                bool ascending = true;
                if (moreProperties == false)
                {
                    if (parser.AdvanceIfIdent(DESC) || parser.AdvanceIfIdent(DESCENDING))
                    {
                        ascending      = false;
                        moreProperties = parser.AdvanceIfSymbol(',');
                    }
                    else if (parser.AdvanceIfIdent(ASC) || parser.AdvanceIfIdent(ASCENDING))
                    {
                        moreProperties = parser.AdvanceIfSymbol(',');
                    }
                }

                try
                {
                    comparisons.Add(GetPropertyComparison(propertyParts, ascending));
                }
                catch (ArgumentException ex)
                {
                    throw new ParserException(parser.Position, parser.Expression, ex.Message);
                }
                propertyParts.Clear();
            }while (moreProperties);
            parser.ExpectEnd();
            return(comparisons);
        }
        /// <summary>
        /// Sorts the elements of a queryable sequence based on the given search criteria.
        /// </summary>
        /// <param name="source">The IQueryable sequence to be sorted.</param>
        /// <param name="sortExpression">A SQL-like sort expression with comma separated property names (and optional direction specifiers) (e.g. "Age DESC, Name.Length")</param>
        /// <returns>A queryable sequence sorted according to the sort expression</returns>
        /// <exception cref="System.ArgumentNullException">source or sortExpression is null.</exception>
        /// <exception cref="ParserException">if sortExpression is not properly formatted or contains unrecognized property or field names..</exception>
        public static IOrderedQueryable <T> OrderBy(IQueryable <T> source, String sortExpression)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            if (sortExpression == null)
            {
                throw new ArgumentNullException("sortExpression");
            }

            SimpleTokenizer tokenizer = new SimpleTokenizer(sortExpression);

            IQueryable <T> result = source;

            List <String> propParts = new List <string>(4);

            do
            {
                ParameterExpression param = Expression.Parameter(typeof(T), "o");

                // Create (nested) member access expression.
                Expression body = param;
                do
                {
                    String property = tokenizer.ReadIdentity();
                    if (property.Length == 0)
                    {
                        throw new ParserException(tokenizer.Position, sortExpression, "Property or field expected.");
                    }

                    // Implicitely call Value for Nullable properties/fields.
                    if (Nullable.GetUnderlyingType(body.Type) != null)
                    {
                        body = Expression.Property(body, "Value");
                    }

                    MemberInfo member = GetMemberByName(body.Type, property);
                    if (member == null)
                    {
                        throw new ParserException(tokenizer.Position, sortExpression, property + " not a public property or field.");
                    }

                    body = Expression.MakeMemberAccess(body, member);
                }while (tokenizer.AdvanceIfSymbol('.'));

                LambdaExpression keySelectorLambda = Expression.Lambda(body, param);

                bool ascending = true;
                if (tokenizer.AdvanceIfIdent(DESC) || tokenizer.AdvanceIfIdent(DESCENDING))
                {
                    ascending = false;
                }
                else if (tokenizer.AdvanceIfIdent(ASC) || tokenizer.AdvanceIfIdent(ASCENDING))
                {
                    ascending = true;
                }
                String queryMethod;
                if (result == source)
                {
                    queryMethod = ascending ? "OrderBy" : "OrderByDescending";
                }
                else
                {
                    queryMethod = ascending ? "ThenBy" : "ThenByDescending";
                }

                result = result.Provider.CreateQuery <T>(Expression.Call(typeof(Queryable), queryMethod,
                                                                         new Type[] { typeof(T), body.Type },
                                                                         result.Expression,
                                                                         Expression.Quote(keySelectorLambda)));
            }while(tokenizer.AdvanceIfSymbol(","));
            tokenizer.ExpectEnd();
            return((IOrderedQueryable <T>)result);
        }