Link to home
Start Free TrialLog in
Avatar of flynny
flynnyFlag for United Kingdom of Great Britain and Northern Ireland

asked on

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?
Avatar of flynny
flynny
Flag of United Kingdom of Great Britain and Northern Ireland image

ASKER

Avatar of it_saige
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 -User generated image
HTH,

-saige-
Avatar of flynny

ASKER

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?
I understand now what you are after.  I did something similar at work.  I'll look it up and post a solution tommorow.

-saige-
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 -User generated image
-saige-
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial