private string BuildOrderBy(TypeMap type, object sortOrders)
        {
            // coalesce the dictionary
            IDictionary <string, SortOrder> sortOrderDict = type.CoalesceSortOrderDictionary(sortOrders);

            // validate / return
            StringBuilder orderBySb = new StringBuilder("ORDER BY ");

            for (int i = 0; i < sortOrderDict.Count; i++)
            {
                string    propertyName = sortOrderDict.Keys.ElementAt(i);
                SortOrder order        = sortOrderDict[propertyName];

                if (type.AllProperties.TryGetValue(propertyName, out PropertyMap pm) == false)
                {
                    throw new ArgumentException($"Failed to find property {propertyName} on {type.Type.Name}");
                }

                orderBySb.Append($"{pm.Column} {this.GetSortOrder(order)}");

                if (i != sortOrderDict.Count - 1)
                {
                    orderBySb.Append(", ");
                }
            }

            return(orderBySb.ToString());
        }
        /// <inheritdoc />
        public Task <PagedList <T> > ReadList <T>(object whereConditions, object sortOrders, int pageSize, int pageNumber)
        {
            TypeMap type = TypeMap.GetTypeMap <T>();

            // create the paging variables
            int firstRow = ((pageNumber - 1) * pageSize) + 1;
            int lastRow  = firstRow + (pageSize - 1);

            // get the candidate objects
            IEnumerable <T> filteredT;
            IDictionary <string, object> whereDict = type.CoalesceToDictionary(whereConditions);

            if (whereDict.Count == 0)
            {
                filteredT = this.ReadAll <T>().GetAwaiter().GetResult();
            }
            else
            {
                filteredT = this.ReadList <T>(whereConditions).GetAwaiter().GetResult();
            }

            // get the total number of candidate objects
            int total = filteredT.Count();

            // validate / build the ordering string
            string ordering = string.Empty;
            IDictionary <string, SortOrder> sortOrderDict = type.CoalesceSortOrderDictionary(sortOrders);

            for (int i = 0; i < sortOrderDict.Count; i++)
            {
                // check whether this property exists for the type
                string propertyName = sortOrderDict.Keys.ElementAt(i);
                if (!type.AllProperties.ContainsKey(propertyName))
                {
                    throw new ArgumentException($"Failed to find property {propertyName} on {type.Type.Name}");
                }

                ordering += string.Format(
                    "{0}{1}{2}",
                    propertyName,
                    sortOrderDict[propertyName] == SortOrder.Descending ? " desc" : string.Empty,
                    i != sortOrderDict.Count - 1 ? "," : string.Empty);
            }

            // order the rows and take the results for this page
            filteredT = filteredT.AsQueryable <T>().OrderBy(ordering).Skip(firstRow - 1).Take(pageSize);

            return(Task.FromResult(new PagedList <T>()
            {
                Rows = filteredT,
                HasNext = lastRow < total,
                HasPrevious = firstRow > 1,
                TotalPages = (total / pageSize) + ((total % pageSize) > 0 ? 1 : 0),
                TotalRows = total
            }));
        }