Tentando filtrar em um tipo anulável usando árvores de expressão

Eu colei meu aplicativo de teste inteiro abaixo. É bastante compacto, então espero que não seja um problema. Você deve ser capaz de simplesmente cortar e colar em um aplicativo de console e executá-lo.

Eu preciso ser capaz de filtrar em qualquer uma ou mais das propriedades dos objetos Person, e não sei qual (is) até o tempo de execução. Eu sei que isso tem sido discutido em todo o lugar e eu olhei e também estou usando ferramentas como oPredicateBuilder & Biblioteca Linq Dinâmica mas a discussão em torno deles tende a se concentrar mais em classificação e ordenação, e cada um tem lutado com seus próprios problemas quando confrontados com tipos Nullable. Então, pensei em tentar criar pelo menos um filtro suplementar que pudesse abordar esses cenários específicos.

No exemplo abaixo, estou tentando filtrar os membros da família que nasceram depois de uma certa data. O chute é que o campo DateOfBirth nos objetos que estão sendo filtrados é uma propriedade DateTime.

O último erro que estou recebendo é

Nenhum operador de coerção é definido entre os tipos 'System.String' e 'System.Nullable`1 [System.DateTime]'.

Qual é o problema? Eu tentei vários meios diferentes de conversão e conversão, mas em graus variados de falha. Em última análise, isso será aplicado a um banco de dados EF, que também recusou métodos de conversão como DateTime.Parse (-).

Qualquer assistência seria muito apreciada!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Person> people = new List<Person>();
        people.Add(new Person { FirstName = "Bob", LastName = "Smith", DateOfBirth = DateTime.Parse("1969/01/21"), Weight=207 });
        people.Add(new Person { FirstName = "Lisa", LastName = "Smith", DateOfBirth = DateTime.Parse("1974/05/09") });
        people.Add(new Person { FirstName = "Jane", LastName = "Smith", DateOfBirth = DateTime.Parse("1999/05/09") });
        people.Add(new Person { FirstName = "Lori", LastName = "Jones", DateOfBirth = DateTime.Parse("2002/10/21") });
        people.Add(new Person { FirstName = "Patty", LastName = "Smith", DateOfBirth = DateTime.Parse("2012/03/11") });
        people.Add(new Person { FirstName = "George", LastName = "Smith", DateOfBirth = DateTime.Parse("2013/06/18"), Weight=6 });

            String filterField = "DateOfBirth";
            String filterOper = "<=";
            String filterValue = "2000/01/01";

            var oldFamily = ApplyFilter<Person>(filterField, filterOper, filterValue);

            var query = from p in people.AsQueryable().Where(oldFamily) 
                        select p;

            Console.ReadLine();
        }

        public static Expression<Func<T, bool>> ApplyFilter<T>(String filterField, String filterOper, String filterValue)
        {
            //
            // Get the property that we are attempting to filter on. If it does not exist then throw an exception
            System.Reflection.PropertyInfo prop = typeof(T).GetProperty(filterField);
            if (prop == null)
                throw new MissingMemberException(String.Format("{0} is not a member of {1}", filterField, typeof(T).ToString()));

            Expression convertExpression     = Expression.Convert(Expression.Constant(filterValue), prop.PropertyType);

            ParameterExpression parameter    = Expression.Parameter(prop.PropertyType, filterField);
            ParameterExpression[] parameters = new ParameterExpression[] { parameter };
            BinaryExpression body            = Expression.LessThanOrEqual(parameter, convertExpression);


            Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(body, parameters);


            return predicate;

        }
    }

    public class Person
    {

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? DateOfBirth { get; set; }
        string Nickname { get; set; }
        public int? Weight { get; set; }

        public Person() { }
        public Person(string fName, string lName)
        {
            FirstName = fName;
            LastName = lName;
        }
    }
}

Atualização: 2013/02/01

Meu pensamento foi então converter o tipo Nullabe para a versão do tipo Não-Nulo. Portanto, nesse caso, queremos converter o <Nullable> DateTime em um tipo DateTime simples. Eu adicionei o seguinte bloco de código antes da chamada Expression.Convert chamada para determinar e capturar o tipo do valor anulável.

//
//
Type propType = prop.PropertyType;
//
// If the property is nullable we need to create the expression using a NON-Nullable version of the type.
// We will get this by parsing the type from the FullName of the type 
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    String typeName = prop.PropertyType.FullName;
    Int32 startIdx  = typeName.IndexOf("[[") + 2;
    Int32 endIdx    = typeName.IndexOf(",", startIdx);
    String type     = typeName.Substring(startIdx, (endIdx-startIdx));
    propType        = Type.GetType(type);
}

Expression convertExpression = Expression.Convert(Expression.Constant(filterValue), propType);

Isso realmente funcionou na remoção do Nullable-ness do DateTime, mas resultou no seguinte erro de coerção. Eu permaneço confuso com isso, pois achei que o propósito do método "Expression.Convert" era fazer exatamente isso.

Nenhum operador de coerção é definido entre os tipos 'System.String' e 'System.DateTime'.

Empurrando, eu analisei explicitamente o valor para um DateTime e liguei isso na mistura ...

DateTime dt = DateTime.Parse(filterValue);
Expression convertExpression = Expression.Convert(Expression.Constant(dt), propType);

... o que resultou em uma exceção que supera qualquer conhecimento que tenho de expressões, Lambdas e suas ilk relacionadas ...

ParameterExpression do tipo 'System.DateTime' não pode ser usado para o parâmetro delegate do tipo 'ConsoleApplication1.Person'

Não tenho certeza do que resta para tentar.

questionAnswers(2)

yourAnswerToTheQuestion