Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C# 3.5
Tip/Trick

Dynamically creating a predicate delegate

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
29 May 2014CPOL1 min read 21.6K   91   7   4
Created a POC that dynamically builds a predicate using Expression and Reflection.

Introduction

Recently, I worked on a task that involved creating a small search method. The application this was for uses an ORM and LINQ to query the database. I decided to look into building an Expression tree, After building my POC that proved building a dynamic predicate worked well, I completed my task. I decided to upload my POC. If I can help the next person with a similar project, that is great. For some, this may be very elementary, for others, a great kick start.

Using the code

The code uses a generic list of User class (entity/model) that is used to search, and a Search class where the dynamic Expression tree is built and the predicate created. Within the upload, I created a console app that brings this all together. Below are the contents of the Search used to build the predicate and the User class to search against within a list. I have also uploaded my POC.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MyPredicateBuilder
{
    #region Search Class

    public class Search
    {
        private List<User> _users;
        public Search(List<User> users)
        {
            _users = users;
        }

        public List<User> Results { get; private set; }

        public List<User> For(string firstName = "", string lastName = "", string email = "", int? userId = null)
        {
            var result = new List<User>();

            //start building expression tree 

            //   Starting out with specifying the Entity/Model to be searched within the generic list.  
            //   In this case, we start with User list.
            var li = Expression.Parameter(typeof(User), "User");
            Expression where = null;

            if (!String.IsNullOrEmpty(firstName))
            {
                AndAlso(NameSearch(li, "First", firstName), ref where);
            }
            if (!String.IsNullOrEmpty(lastName))
            {
                AndAlso(NameSearch(li, "Last", lastName), ref where);
            }

            if (!String.IsNullOrEmpty(email))
            {
                AndAlso(EmailSearch(li, email), ref where);
            }
            if (userId.HasValue)
            {
                AndAlso(UserIdSearch(li, userId.Value), ref where);
            }

            if (where == null)
            {
                return _users;
            }

            var predicate = Expression.Lambda<Func<User, bool>>(where, new ParameterExpression[] { li }).Compile();

            return _users.Where(predicate).ToList();
        }

        private void AndAlso(Expression equals, ref Expression op)
        {
            if (op == null)
            {
                op = equals;
            }
            else
            {
                op = Expression.AndAlso(op, equals);
            }
        }

        /// <summary>
        /// Search first/last user name
        /// </summary>
        /// <param name="listOfNames"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private Expression NameSearch(Expression listOfNames, string propertyName, string value)
        {

            //  a. get expression for Name object within list
            var nameObjInList = Expression.Property(listOfNames, "Name");

            // b. get expression of property found in Name object
            var nameProperty = Expression.Property(nameObjInList, propertyName);

            // c. get MethodInfo of the StartWith method found in the String object
            var startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });

            // d. get expression that represents the name value to search 
            var nameSearch = Expression.Constant(value, typeof(string));

            // e. get the start with expression call
            var startWithCall = Expression.Call(nameProperty, startsWithMethod, new Expression[1] { nameSearch });

            // f. get expression that represents the value for the StartWith return upon executing
            var right = Expression.Constant(true, typeof(bool));

            // g. return final equals expression to help build predicate
            return Expression.Equal(startWithCall, right);

        }


        /// <summary>
        /// Search email within list of emails
        /// </summary>
        /// <param name="listOfNames"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private Expression EmailSearch(Expression listOfNames, string value)
        {
            //a. Create expression predicate. 
            Expression<Predicate<string>> emailPred = e => e.StartsWith(value);

            //b. Create expression property for User propery Emails list
            var ep = Expression.Property(listOfNames, "Emails");

            //c. Create constant value of return
            var isTrue = Expression.Constant(true, typeof(bool));

            //d. Get methodinfo for string List<string>.Exists(..) method
            var existsMethod = typeof(List<string>).GetMethod("Exists", new[] { typeof(Predicate<string>) });

            //e. Create expression call
            var existsCall = Expression.Call(ep, existsMethod, new Expression[1] { emailPred });

            //f. return comparison expression
            return Expression.Equal(existsCall, isTrue);

        }

        private Expression UserIdSearch(Expression listOfNames, int userId)
        {
            var userIdParam = Expression.Property(listOfNames, "UserId");
            var userIdValue = Expression.Constant(userId, typeof(int));

            return Expression.Equal(userIdParam, userIdValue);
        }
    }
    #endregion

    #region User Entity

    public class UserName
    {
        public string First { get; set; }

        public string Last { get; set; }
    }

    public class User
    {
        public User()
        {
            Emails = new List<String>();
            Name = new UserName();
        }

        public int UserId { get; set; }

        public UserName Name { get; set; }

        public List<string> Emails { get; set; }
    }

    #endregion
}

Points of Interest

This was my first time using the Expression object and Reflection. After poking around more, I also saw a PredicateBuilder, url: http://www.albahari.com/nutshell/predicatebuilder.aspx. This definitely streamlines doing exactly the same thing. Call me crazy, but I like digging in the trenches (once in a while) especially when the solution I need is easy and straight forward, and I have the time to develop it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 1076371727-Jun-14 4:53
Member 1076371727-Jun-14 4:53 
GeneralRe: My vote of 5 Pin
Randy Kroeger27-Jun-14 17:09
Randy Kroeger27-Jun-14 17:09 
Suggestionorder of filter conditions in predicate Pin
Alexander Sharykin2-Jun-14 21:34
Alexander Sharykin2-Jun-14 21:34 
GeneralRe: order of filter conditions in predicate Pin
Randy Kroeger3-Jun-14 6:17
Randy Kroeger3-Jun-14 6:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.