Skip to content

Instantly share code, notes, and snippets.

@kudchikarsk
Last active July 21, 2020 13:54
Show Gist options
  • Save kudchikarsk/fea3ce276a591f86fd3d5ba74a6a6946 to your computer and use it in GitHub Desktop.
Save kudchikarsk/fea3ce276a591f86fd3d5ba74a6a6946 to your computer and use it in GitHub Desktop.
A extension class for asp.net web api developers. Extension methods for pagination, orderby properties, alpha pagination, and date range filter
using Core.Utils;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
namespace Logic.Utils
{
public static class IQueryableExtensions
{
public static IQueryable<T> WhereNameStartsWith<T>(this IQueryable<T> query, string selectedLetter) where T : class, IName
{
if (String.IsNullOrWhiteSpace(selectedLetter)) return query;
if (selectedLetter == "0-9")
{
var numbers = Enumerable.Range(0, 10).Select(i => i.ToString());
return query = query.Where(p => numbers.Contains(p.Name.Substring(0, 1)));
}
return query.Where(e => e.Name.StartsWith(selectedLetter));
}
public static IQueryable<T> WhereLike<T>(this IQueryable<T> query, string searchTerm,Func<string, Expression<Func<T, bool>>> predicate) where T : class
{
searchTerm = searchTerm ?? "";
var expressions = searchTerm.Split()
.Where(s => !String.IsNullOrWhiteSpace(s))
.Select(s => s.Trim())
.Select(e => "%" + searchTerm + "%")
.ToList();
var idPattern = @"[1-9]\d+";
var costCenterPattern = @"\S+[1-9]\d+";
var costCenterMatch = Regex.Match(searchTerm, costCenterPattern);
if (costCenterMatch.Success && costCenterMatch.Value == searchTerm)
{
var idMatch = Regex.Match(searchTerm, idPattern);
if (idMatch.Success)
{
expressions.Clear();
expressions.Add("%" + idMatch.Value + "%");
}
}
foreach (var likeExpression in expressions)
{
query = query.Where(predicate(likeExpression));
}
return query;
}
public static IQueryable<T> FilterDeleted<T>(this IQueryable<T> query) where T: class, ISoftDelete
{
return query.Where(e => !e.IsDeleted);
}
/// <summary>
/// Creates a paged set of results.
/// </summary>
/// <typeparam name="TEntity">The type of the source IQueryable.</typeparam>
/// <param name="queryable">The source IQueryable.</param>
/// <param name="page">The page number you want to retrieve.</param>
/// <param name="pageSize">The size of the page.</param>
/// <param name="orderBy">The field or property to order by.</param>
/// <param name="ascending">Indicates whether or not the order should be ascending (true) or descending (false.)</param>
/// <returns>Returns a paged set of results.</returns>
public static async Task<PagedResults<TEntity>> PaginateAsync<TEntity>(
this IQueryable<TEntity> dbQuery,
int page,
int pageSize
)
{
var skipAmount = pageSize * (page - 1);
var projection = dbQuery
.Skip(skipAmount)
.Take(pageSize);
var totalNumberOfRecords = await dbQuery.CountAsync();
var results = await projection.ToListAsync();
var mod = totalNumberOfRecords % pageSize;
var totalPageCount = (totalNumberOfRecords / pageSize) + (mod == 0 ? 0 : 1);
return new PagedResults<TEntity>
{
Results = results,
PageNumber = page,
PageSize = pageSize,
ResultCount = results.Count,
TotalNumberOfPages = totalPageCount,
TotalNumberOfRecords = totalNumberOfRecords,
};
}
/// <summary>
/// Order the IQueryable by the given property or field.
/// </summary>
/// <typeparam name="T">The type of the IQueryable being ordered.</typeparam>
/// <param name="queryable">The IQueryable being ordered.</param>
/// <param name="propertyOrFieldName">The name of the property or field to order by.</param>
/// <param name="ascending">Indicates whether or not the order should be ascending (true) or descending (false.)</param>
/// <returns>Returns an IQueryable ordered by the specified field.</returns>
public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable, string propertyOrFieldName, bool ascending = true)
{
var elementType = typeof(T);
var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";
var parameterExpression = Expression.Parameter(elementType);
var propertyOrFieldExpression = GetMemberExpression(parameterExpression, propertyOrFieldName);
var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);
var orderByExpression = Expression.Call(typeof(Queryable), orderByMethodName,
new[] { elementType, propertyOrFieldExpression.Type }, queryable.Expression, selector);
return queryable.Provider.CreateQuery<T>(orderByExpression);
}
public static IQueryable<T> BetweenDates<T>(this IQueryable<T> queryable, string propertyOrFieldName, DateTime? start, DateTime? end)
{
var elementType = typeof(T);
var parameterExpression = Expression.Parameter(elementType);
var propertyOrFieldExpression = GetMemberExpression(parameterExpression, propertyOrFieldName);
var p = Expression.Convert(propertyOrFieldExpression, typeof(DateTime?));
var notNullExpression = Expression.NotEqual(p, Expression.Constant(null, typeof(DateTime?)));
var startExp = Expression.Constant(start, typeof(DateTime?));
var lessThan1 = Expression.LessThanOrEqual(startExp, p);
var endExp = Expression.Constant(end, typeof(DateTime?));
var lessThan2 = Expression.LessThanOrEqual(p, endExp);
var and1 = Expression.And(notNullExpression, lessThan1);
var and2 = Expression.And(and1, lessThan2);
var whereClause = Expression.Lambda<Func<T, bool>>(and2,
new ParameterExpression[] { parameterExpression });
var dateFilterExpression = Expression.Call(typeof(Queryable), "Where",
new[] { elementType }, queryable.Expression, whereClause);
return queryable.Provider.CreateQuery<T>(dateFilterExpression);
}
public static MemberExpression GetMemberExpression(Expression param, string propertyName)
{
if (propertyName.Contains("."))
{
int index = propertyName.IndexOf(".");
var subParam = Expression.Property(param, propertyName.Substring(0, index));
return GetMemberExpression(subParam, propertyName.Substring(index + 1));
}
return Expression.Property(param, propertyName);
}
}
}
@kudchikarsk
Copy link
Author

kudchikarsk commented Jul 21, 2020

using System.Collections.Generic;

namespace Logic.Utils
{
    public class PagedResults<T>
    {
        /// <summary>
        /// The page number this page represents. 
        /// </summary>
        public int PageNumber { get; set; }

        /// <summary> 
        /// The size of this page. 
        /// </summary> 
        public int PageSize { get; set; }

        /// <summary> 
        /// The total number of pages available. 
        /// </summary> 
        public int TotalNumberOfPages { get; set; }

        /// <summary> 
        /// The total number of records available. 
        /// </summary> 
        public int TotalNumberOfRecords { get; set; }

        /// <summary> 
        /// The records this page represents. 
        /// </summary> 
        public IEnumerable<T> Results { get; set; }

        /// <summary> 
        /// The count of results. 
        /// </summary> 
        public int ResultCount { get; set; }


    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment