Building Generic Expression Seach

Hi All,

I have implemented the following generic search to a repository;

Now I am trying to extend this so that I can filter the results based on an expression. However, I'm new to these and so unsrue what to do.

The method is as follows;

        public virtual IOrderedEnumerable<T> GetPage(int pageIndex = 0, int pageSize = 10, string sortBy = "ID", string sortDirection = "asc", Expression<Func<T, bool>> predicate = null)
        {
            var param = Expression.Parameter(typeof(T));

            var sortExpression = Expression.Lambda<Func<T, object>>
                (Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param).Compile();

            var results = _dbset.OrderBy(sortExpression).Skip((pageIndex - 1) * pageSize).Take(pageSize);

            switch (sortDirection.ToLower())
            {
                case "asc":
                    return results.OrderBy(sortExpression);
                default:
                    return results.OrderByDescending(sortExpression);
            }
        }

Open in new window


So for example I may want to pass across one or more constraints on the data.

e.g. date = 01/01/19, name = "filtername", archived = false

Coudl someone point me in the right direction?
flynnyAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

flynnyAuthor Commented:
it_saigeDeveloperCommented:
Hi flynny,

I answered a question that was similar to this for someone else.  Your best bet is to use another class that will hold the name of the property to sort on and the sort direction.  Then you can dynamically build out the sort expression.

Illustrated in VB.NET here - http:/Q_28675957.html

For a C# version you are probably looking to do something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace EE_Q28695557
{
	class Program
	{
		static void Main(string[] args)
		{
			var people = new List<Person>() 
			{
				new Person() {ID = 0, FirstName = "Zed", LastName = "Anderson", Birthday = new DateTime(2014, 1, 1)},
				new Person() {ID = 1, FirstName = "Yancy", LastName = "Benson", Birthday = new DateTime(2003, 1, 1)},
				new Person() {ID = 2, FirstName = "Xarius", LastName = "Centiller", Birthday = new DateTime(2012, 1, 1)},
				new Person() {ID = 3, FirstName = "Victor", LastName = "Davidson", Birthday = new DateTime(2005, 1, 1)},
				new Person() {ID = 4, FirstName = "Ullyses", LastName = "Eaton", Birthday = new DateTime(2010, 1, 1)},
				new Person() {ID = 5, FirstName = "Terry", LastName = "Foster", Birthday = new DateTime(2007, 1, 1)},
				new Person() {ID = 6, FirstName = "Simon", LastName = "Guillen", Birthday = new DateTime(2008, 1, 1)},
				new Person() {ID = 7, FirstName = "Randolph", LastName = "Harrison", Birthday = new DateTime(2009, 1, 1)},
				new Person() {ID = 8, FirstName = "Quincy", LastName = "Iverson", Birthday = new DateTime(2006, 1, 1)},
				new Person() {ID = 9, FirstName = "Paul", LastName = "Jones", Birthday = new DateTime(2011, 1, 1)},
				new Person() {ID = 10, FirstName = "Oscar", LastName = "Kite", Birthday = new DateTime(2004, 1, 1)},
				new Person() {ID = 11, FirstName = "Norman", LastName = "Lyle", Birthday = new DateTime(2013, 1, 1)},
				new Person() {ID = 12, FirstName = "Mary", LastName = "Margaret", Birthday = new DateTime(2002, 1, 1)},
			};

			Console.WriteLine("Ordering the first page of results by descending ID:");
			foreach (var p in people.GetPage(1, 5).GetOrdered("ID", SortDirection.Descending))
				Console.WriteLine(p);

			Console.WriteLine();
			Console.WriteLine("Now ordering the last page of results by ascending First Name, descending Birthday and descending Last Name:");
			foreach (var p in people.GetPage(3, 5).GetOrdered(new List<SortOption>() { new SortOption("FirstName"), new SortOption("Birthday", SortDirection.Descending), new SortOption("LastName", SortDirection.Descending) }))
				Console.WriteLine(p);

			Console.ReadLine();
		}
	}

	class Person
	{
		public int ID { get; set; }
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public DateTime Birthday { get; set; }

		public override string ToString()
		{
			return string.Format("{0} {1}; Born on - {2}", FirstName, LastName, Birthday.ToShortDateString());
		}
	}

	static class Extensions
	{
		public static IEnumerable<T> GetPage<T>(this IEnumerable<T> source, int index = 0, int size = 10)
		{
			return source.Skip((index - 1) * size).Take(size);
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, string name, SortDirection direction)
		{
			if (string.IsNullOrEmpty(name))
				return source;
			return source.GetOrdered(new SortOption(name, direction));
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, SortOption option)
		{
			if (option == null)
				return source;
			return source.GetOrdered(new List<SortOption>() { option });
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, IEnumerable<SortOption> options)
		{
			IEnumerable<T> results = source;
			bool firstExpression = true;

			if (options != null)
			{
				foreach (var option in options)
				{
					switch (option.Direction)
					{
						case SortDirection.None:
						case SortDirection.Ascending:
							if (firstExpression)
								results = results.OrderBy(typeof(T).GetOrderedByFunction<T>(option.Name));
							else
								results = ((IOrderedEnumerable<T>)results).ThenBy(typeof(T).GetOrderedByFunction<T>(option.Name));
							break;
						case SortDirection.Descending:
							if (firstExpression)
								results = results.OrderByDescending(typeof(T).GetOrderedByFunction<T>(option.Name));
							else
								results = ((IOrderedEnumerable<T>)results).ThenByDescending(typeof(T).GetOrderedByFunction<T>(option.Name));
							break;
						default:
							break;
					}
					if (firstExpression)
						firstExpression = false;
				}
			}
			return results;
		}

		public static Func<T, object> GetOrderedByFunction<T>(this Type source, string value)
		{
			ParameterExpression parameter = null;
			Expression conversion = null;
			Func<T, object> result = null;

			if (!string.IsNullOrEmpty(value))
			{
				parameter = Expression.Parameter(source, "x");
				conversion = Expression.Convert(Expression.Property(parameter, value), typeof(object));
				result = Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
			}
			return result;
		}
	}

	class SortOption
	{
		private readonly string fName = string.Empty;
		private readonly SortDirection fDirection = SortDirection.None;

		public string Name { get { return fName; } }
		public SortDirection Direction { get { return fDirection; } }

		private SortOption() { ;}
		public SortOption(string Name) : this(Name, SortDirection.Ascending) { ;}
		public SortOption(string Name, SortDirection Direction)
		{
			fName = Name;
			fDirection = Direction.Equals(SortDirection.Descending) ? Direction : SortDirection.Ascending;
		}
	}

	enum SortDirection
	{
		None = 0,
		Ascending = 1,
		Descending = 2
	}
}

Open in new window

Which produces the following output -Capture.JPG
HTH,

-saige-
flynnyAuthor Commented:
Saige,

thanks for that but what about filtering based on predicates in this case?

i.e. firstname="x" and lastname="y"

where we dont know how many predicates are going to be applied?
Learn Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

it_saigeDeveloperCommented:
I understand now what you are after.  I did something similar at work.  I'll look it up and post a solution tommorow.

-saige-
it_saigeDeveloperCommented:
How does this suite you:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace EE_Q28695557
{
	class Program
	{
		static void Main(string[] args)
		{
			var people = new List<Person>() 
			{
				new Person() {ID = 0, FirstName = "Zed", LastName = "Anderson", Birthday = new DateTime(2014, 1, 1)},
				new Person() {ID = 1, FirstName = "Yancy", LastName = "Benson", Birthday = new DateTime(2003, 1, 1)},
				new Person() {ID = 2, FirstName = "Xarius", LastName = "Centiller", Birthday = new DateTime(2012, 1, 1)},
				new Person() {ID = 3, FirstName = "Victor", LastName = "Davidson", Birthday = new DateTime(2005, 1, 1)},
				new Person() {ID = 4, FirstName = "Ullyses", LastName = "Eaton", Birthday = new DateTime(2010, 1, 1)},
				new Person() {ID = 5, FirstName = "Terry", LastName = "Foster", Birthday = new DateTime(2007, 1, 1)},
				new Person() {ID = 6, FirstName = "Simon", LastName = "Guillen", Birthday = new DateTime(2008, 1, 1)},
				new Person() {ID = 7, FirstName = "Randolph", LastName = "Harrison", Birthday = new DateTime(2009, 1, 1)},
				new Person() {ID = 8, FirstName = "Quincy", LastName = "Iverson", Birthday = new DateTime(2006, 1, 1)},
				new Person() {ID = 9, FirstName = "Paul", LastName = "Jones", Birthday = new DateTime(2011, 1, 1)},
				new Person() {ID = 10, FirstName = "Oscar", LastName = "Kite", Birthday = new DateTime(2004, 1, 1)},
				new Person() {ID = 11, FirstName = "Norman", LastName = "Lyle", Birthday = new DateTime(2013, 1, 1)},
				new Person() {ID = 12, FirstName = "Mary", LastName = "Margaret", Birthday = new DateTime(2002, 1, 1)},
			};

			Console.WriteLine("Ordering the first page of results by descending ID:");
			foreach (var p in people.GetPage(1, 5).GetOrdered("ID", SortDirection.Descending))
				Console.WriteLine(p);

			Console.WriteLine();
			Console.WriteLine("Now ordering the last page of results by ascending First Name, descending Birthday and descending Last Name:");
			foreach (var p in people.GetPage(3, 5).GetOrdered(new List<SortOption>() { new SortOption("FirstName"), new SortOption("Birthday", SortDirection.Descending), new SortOption("LastName", SortDirection.Descending) }))
				Console.WriteLine(p);

			Console.WriteLine();
			Console.WriteLine("Now let's get a filtered result:");
			foreach (var p in people.GetFiltered(x => (x.ID <= 3 || x.ID >= 9) && x.FirstName.StartsWith("x", StringComparison.OrdinalIgnoreCase) || x.LastName.StartsWith("k", StringComparison.OrdinalIgnoreCase)))
				Console.WriteLine(p);

			Console.ReadLine();
		}
	}

	class Person
	{
		public int ID { get; set; }
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public DateTime Birthday { get; set; }

		public override string ToString()
		{
			return string.Format("{0} {1}; Born on - {2}", FirstName, LastName, Birthday.ToShortDateString());
		}
	}

	static class Extensions
	{
		public static IEnumerable<T> GetFiltered<T>(this IEnumerable<T> source, WhereFunction<T> filter)
		{
			return source = source.Where(x => filter(x));
		}

		public static IEnumerable<T> GetPage<T>(this IEnumerable<T> source, int index = 0, int size = 10)
		{
			return source.Skip((index - 1) * size).Take(size);
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, string name, SortDirection direction)
		{
			if (string.IsNullOrEmpty(name))
				return source;
			return source.GetOrdered(new SortOption(name, direction));
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, SortOption option)
		{
			if (option == null)
				return source;
			return source.GetOrdered(new List<SortOption>() { option });
		}

		public static IEnumerable<T> GetOrdered<T>(this IEnumerable<T> source, IEnumerable<SortOption> options)
		{
			IEnumerable<T> results = source;
			bool firstExpression = true;

			if (options != null)
			{
				foreach (var option in options)
				{
					switch (option.Direction)
					{
						case SortDirection.None:
						case SortDirection.Ascending:
							if (firstExpression)
								results = results.OrderBy(typeof(T).GetOrderedByFunction<T>(option.Name));
							else
								results = ((IOrderedEnumerable<T>)results).ThenBy(typeof(T).GetOrderedByFunction<T>(option.Name));
							break;
						case SortDirection.Descending:
							if (firstExpression)
								results = results.OrderByDescending(typeof(T).GetOrderedByFunction<T>(option.Name));
							else
								results = ((IOrderedEnumerable<T>)results).ThenByDescending(typeof(T).GetOrderedByFunction<T>(option.Name));
							break;
						default:
							break;
					}
					if (firstExpression)
						firstExpression = false;
				}
			}
			return results;
		}

		public static Func<T, object> GetOrderedByFunction<T>(this Type source, string value)
		{
			ParameterExpression parameter = null;
			Expression conversion = null;
			Func<T, object> result = null;

			if (!string.IsNullOrEmpty(value))
			{
				parameter = Expression.Parameter(source, "x");
				conversion = Expression.Convert(Expression.Property(parameter, value), typeof(object));
				result = Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
			}
			return result;
		}
	}

	delegate bool WhereFunction<T>(T item);

	class SortOption
	{
		private readonly string fName = string.Empty;
		private readonly SortDirection fDirection = SortDirection.None;

		public string Name { get { return fName; } }
		public SortDirection Direction { get { return fDirection; } }

		private SortOption() { ;}
		public SortOption(string Name) : this(Name, SortDirection.Ascending) { ;}
		public SortOption(string Name, SortDirection Direction)
		{
			fName = Name;
			fDirection = Direction.Equals(SortDirection.Descending) ? Direction : SortDirection.Ascending;
		}
	}

	enum SortDirection
	{
		None = 0,
		Ascending = 1,
		Descending = 2
	}
}

Open in new window

Produces the following output -Capture.JPG
-saige-
it_saigeDeveloperCommented:
Implementing it in your current code would look something like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace EE_Q28695557
{
	class Program
	{
		private static List<Person> people = new List<Person>();
		static void Main(string[] args)
		{
			people = new List<Person>() 
			{
				new Person() {ID = 0, FirstName = "Zed", LastName = "Anderson", Birthday = new DateTime(2014, 1, 1)},
				new Person() {ID = 1, FirstName = "Yancy", LastName = "Benson", Birthday = new DateTime(2003, 1, 1)},
				new Person() {ID = 2, FirstName = "Xarius", LastName = "Centiller", Birthday = new DateTime(2012, 1, 1)},
				new Person() {ID = 3, FirstName = "Victor", LastName = "Davidson", Birthday = new DateTime(2005, 1, 1)},
				new Person() {ID = 4, FirstName = "Ullyses", LastName = "Eaton", Birthday = new DateTime(2010, 1, 1)},
				new Person() {ID = 5, FirstName = "Terry", LastName = "Foster", Birthday = new DateTime(2007, 1, 1)},
				new Person() {ID = 6, FirstName = "Simon", LastName = "Guillen", Birthday = new DateTime(2008, 1, 1)},
				new Person() {ID = 7, FirstName = "Randolph", LastName = "Harrison", Birthday = new DateTime(2009, 1, 1)},
				new Person() {ID = 8, FirstName = "Quincy", LastName = "Iverson", Birthday = new DateTime(2006, 1, 1)},
				new Person() {ID = 9, FirstName = "Paul", LastName = "Jones", Birthday = new DateTime(2011, 1, 1)},
				new Person() {ID = 10, FirstName = "Oscar", LastName = "Kite", Birthday = new DateTime(2004, 1, 1)},
				new Person() {ID = 11, FirstName = "Norman", LastName = "Lyle", Birthday = new DateTime(2013, 1, 1)},
				new Person() {ID = 12, FirstName = "Mary", LastName = "Margaret", Birthday = new DateTime(2002, 1, 1)},
			};

			Console.WriteLine("Get an ordered, filtered page:");
			foreach (var p in GetPage(people, 0, 10, "FirstName", "asc", x => x.ID <= 3 || x.ID >= 9))
				Console.WriteLine(p);

			Console.ReadLine();
		}

		public static IOrderedEnumerable<T> GetPage<T>(IEnumerable<T> source, int pageIndex = 0, int pageSize = 10, string sortBy = "ID", string sortDirection = "asc", WhereFunction<T> predicate = null)
		{
			var param = Expression.Parameter(typeof(T), "x");
			var sortExpression = Expression.Lambda<Func<T, object>>
			    (Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param).Compile();

			var results = source;

			if (predicate != null)
				results = results.Where(x => predicate(x));

			results = results.OrderBy(sortExpression).Skip((pageIndex - 1) * pageSize).Take(pageSize);

			switch (sortDirection.ToLower())
			{
				case "asc":
					return results.OrderBy(sortExpression);
				default:
					return results.OrderByDescending(sortExpression);
			}
		}
	}

	delegate bool WhereFunction<T>(T item);


	class Person
	{
		public int ID { get; set; }
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public DateTime Birthday { get; set; }

		public override string ToString()
		{
			return string.Format("{0} {1}; Born on - {2}", FirstName, LastName, Birthday.ToShortDateString());
		}
	}
}

Open in new window

Which produces the following output -Capture.JPG
-saige-

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
ASP.NET

From novice to tech pro — start learning today.