Dynamic LINQ Query In C# Using Predicate Builder

Introduction

This tutorial explains how to create a dynamic LINQ query in C#. Using the Predicate Builder we can create LINQ to SQL dynamic query and Query with Entity Framework is easy. This concept was first implement by albahari. Later this concept extended by Monty’s Gush as Universal PredicateBuilder.

Description

Predicate Builder is a powerful LINQ expression that is mainly used when too many search filter parameters are used for querying data by writing dynamic query expression. We can write a query like Dynamic SQL.

To learn more about predicate delegate visit Predicate Delegate.

How to use predicate builder to create dynamic linq query

Model
  1. public class PatientInfo    
  2. {    
  3.     public int PatientID { get; set; }    
  4.     [Required]    
  5.     public string FirstName { get; set; }    
  6.     [Required]    
  7.     public string LastName { get; set; }    
  8.     [Required]    
  9.     [DataType(DataType.DateTime)]    
  10.     public Nullable<System.DateTime> BirthDate { get; set; }    
  11.     public string Gender { get; set; }    
  12.     public string PatientType { get; set; }    
  13.     public string InsuranceNumber { get; set; }    
  14.     [Required]    
  15.     [DataType(DataType.DateTime)]    
  16.     public Nullable<System.DateTime> AdmissionDate { get; set; }    
  17.     public bool IsHaveInsurence { get; set; }    
  18. }    
Namespace
  1. using System;  
  2. using System.Data;  
  3. using System.Data.Objects;  
  4. using System.Data.Entity;  
  5. using System.Linq;  
  6. using System.Web.Mvc;  
Implementation
  1. public ActionResult Index(string PatientName, string BirthDate, string Gender, string PatientType)  
  2. {  
  3.     ViewBag.PatientName = PatientName ?? "";  
  4.     ViewBag.BirthDate = BirthDate ?? "";  
  5.     ViewBag.Gender = Gender ?? "";  
  6.     ViewBag.PatientType = PatientType ?? "";  
  7.   
  8.     var predicate = PredicateBuilder.True<Patient>();  
  9.   
  10.     if (!string.IsNullOrEmpty(PatientName))  
  11.     {  
  12.         predicate = predicate.And(i => i.FirstName.ToLower().StartsWith(PatientName) || i.LastName.ToLower().StartsWith(PatientName));  
  13.     }  
  14.   
  15.     if (!string.IsNullOrEmpty(Gender))  
  16.     {  
  17.         int gender;  
  18.         Int32.TryParse(Gender, out gender);  
  19.         predicate = predicate.And(i => i.Gender == gender);  
  20.     }  
  21.     if (!string.IsNullOrEmpty(PatientType))  
  22.     {  
  23.         int type;  
  24.         Int32.TryParse(PatientType, out type);  
  25.         predicate = predicate.And(i => i.PatientType == type);  
  26.     }  
  27.   
  28.     if (!string.IsNullOrEmpty(BirthDate))  
  29.     {  
  30.         DateTime dob;  
  31.         DateTime.TryParse(BirthDate, out dob);  
  32.         predicate = predicate.And(i => EntityFunctions.TruncateTime(i.BirthDate) == EntityFunctions.TruncateTime(dob));  
  33.     }  
  34.   
  35.     var patients = db.Patients.Where(predicate).Select(i => i).Include(p => p.DropDownOption).Include(p => p.DropDownOption1);  
  36.     ViewBag.Gender = new SelectList(db.DropDownOptions.Where(i => i.Item == "Gender").Select(i => i), "DropDownID""Name", ViewBag.Gender);  
  37.     ViewBag.PatientType = new SelectList(db.DropDownOptions.Where(i => i.Item == "PatientType").Select(i => i), "DropDownID""Name", ViewBag.PatientType);  
  38.     return View(patients.ToList());  
  39. }   

In this example I have created an instance of PredicateBuilder with PatientInfo Model and added multiple OR and AND conditions based on their value. Predicate Builder automatically creates a dynamic query with LINQ and combines it into one expression.

When we have a grid that filters records based on an applied filter and there are many filter parameters, the decision to use Dynamic LINQ results in much better performance and it minimizes the amount of code to be written for the implementation, otherwise it requires many if/else statements based on the filter parameter.

In the preceding code I have used an Entity Framework Entity function that is very useful when we need to perform DateTime operations. It internally works as a SQL DateTime function.

PrecateBuilder.cs

  1. /// <summary>    
  2. /// Enables the efficient, dynamic composition of query predicates.    
  3. /// </summary>    
  4. public static class PredicateBuilder    
  5. {    
  6.     /// <summary>    
  7.     /// Creates a predicate that evaluates to true.    
  8.     /// </summary>    
  9.     public static Expression<Func<T, bool>> True<T>() { return param => true; }    
  10.     
  11.     /// <summary>    
  12.     /// Creates a predicate that evaluates to false.    
  13.     /// </summary>    
  14.     public static Expression<Func<T, bool>> False<T>() { return param => false; }    
  15.     
  16.     /// <summary>    
  17.     /// Creates a predicate expression from the specified lambda expression.    
  18.     /// </summary>    
  19.     public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }    
  20.     
  21.     /// <summary>    
  22.     /// Combines the first predicate with the second using the logical "and".    
  23.     /// </summary>    
  24.     public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)    
  25.     {    
  26.         return first.Compose(second, Expression.AndAlso);    
  27.     }    
  28.     
  29.     /// <summary>    
  30.     /// Combines the first predicate with the second using the logical "or".    
  31.     /// </summary>    
  32.     public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)    
  33.     {    
  34.         return first.Compose(second, Expression.OrElse);    
  35.     }    
  36.     
  37.     /// <summary>    
  38.     /// Negates the predicate.    
  39.     /// </summary>    
  40.     public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)    
  41.     {    
  42.         var negated = Expression.Not(expression.Body);    
  43.         return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);    
  44.     }    
  45.     
  46.     /// <summary>    
  47.     /// Combines the first expression with the second using the specified merge function.    
  48.     /// </summary>    
  49.     static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)    
  50.     {    
  51.         // zip parameters (map from parameters of second to parameters of first)    
  52.         var map = first.Parameters    
  53.             .Select((f, i) => new { f, s = second.Parameters[i] })    
  54.             .ToDictionary(p => p.s, p => p.f);    
  55.     
  56.         // replace parameters in the second lambda expression with the parameters in the first    
  57.         var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);    
  58.     
  59.         // create a merged lambda expression with parameters from the first expression    
  60.         return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);    
  61.     }    
  62.     
  63.     class ParameterRebinder : ExpressionVisitor    
  64.     {    
  65.         readonly Dictionary<ParameterExpression, ParameterExpression> map;    
  66.     
  67.         ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)    
  68.         {    
  69.             this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();    
  70.         }    
  71.     
  72.         public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)    
  73.         {    
  74.             return new ParameterRebinder(map).Visit(exp);    
  75.         }    
  76.     
  77.         protected override Expression VisitParameter(ParameterExpression p)    
  78.         {    
  79.             ParameterExpression replacement;    
  80.     
  81.             if (map.TryGetValue(p, out replacement))    
  82.             {    
  83.                 p = replacement;    
  84.             }    
  85.     
  86.             return base.VisitParameter(p);    
  87.         }    
  88.     }    
  89. }    

 

The preceding Predicate Builder helper method is referenced from Albahari and this stackoverflow article. Predicate builder also works with IEnumerable and IQueryable.

Reference Links
  1. https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
  2. https://msdn.microsoft.com/en-us/library/bb882521%28v=vs.90%29.aspx
  3. https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/

Conclusion

This article explains the implementation of a Dynamic LINQ query using Predicate Builder. I hope this article is useful when implementing dynamic queries using LINQ.