Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dynamically creating a predicate delegate

0.00/5 (No votes)
29 May 2014 6  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here