Do you want something like this?
static List<Func<TEntity, TCriteria, bool>> GetCriteriaFunctions<TEntity, TCriteria>()
{
var criteriaFunctions = new List<Func<TEntity, TCriteria, bool>>();
// searching for nullable properties of criteria
var criteriaProperties = typeof(TCriteria)
.GetProperties()
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>));
foreach (var property in criteriaProperties)
{
// this is entity parameter
var entityParameterExpression = Expression.Parameter(typeof(TEntity));
// this is criteria parameter
var criteriaParameterExpression = Expression.Parameter(typeof(TCriteria));
// this is criteria property access: "criteria.SomeProperty"
var criteriaPropertyExpression = Expression.Property(criteriaParameterExpression, property);
// this is testing for equality between criteria property and entity property;
// note, that criteria property should be converted first;
// also, this code makes assumption, that entity and criteria properties have the same names
var testingForEqualityExpression = Expression.Equal(
Expression.Convert(criteriaPropertyExpression, property.PropertyType.GetGenericArguments()[0]),
Expression.Property(entityParameterExpression, property.Name));
// criteria.SomeProperty == null ? true : ((EntityPropertyType)criteria.SomeProperty == entity.SomeProperty)
var body = Expression.Condition(
Expression.Equal(criteriaPropertyExpression, Expression.Constant(null)),
Expression.Constant(true),
testingForEqualityExpression);
// let's compile lambda to method
var criteriaFunction = Expression.Lambda<Func<TEntity, TCriteria, bool>>(body, entityParameterExpression, criteriaParameterExpression).Compile();
criteriaFunctions.Add(criteriaFunction);
}
return criteriaFunctions;
}
Sample entity and sample criteria:
class CustomerCriteria
{
public int? Age { get; set; }
public bool? IsNew { get; set; }
}
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsNew { get; set; }
}
Usage:
var criteriaFunctions = GetCriteriaFunctions<Customer, CustomerCriteria>();
var customer1 = new Customer { Name = "John", Age = 35, IsNew = false };
var customer2 = new Customer { Name = "Mary", Age = 27, IsNew = true };
var criteria1 = new CustomerCriteria { Age = 35 };
var criteria2 = new CustomerCriteria { IsNew = true };
Console.WriteLine("Is match: {0}", criteriaFunctions.All(f => f(customer1, criteria1)));
Console.WriteLine("Is match: {0}", criteriaFunctions.All(f => f(customer2, criteria1)));
Console.WriteLine("Is match: {0}", criteriaFunctions.All(f => f(customer1, criteria2)));
Console.WriteLine("Is match: {0}", criteriaFunctions.All(f => f(customer2, criteria2)));
Instead of your code with dynamics, this code uses strongly typed member access, so, you could cache list of criteria for each pair "Entity - Criteria" and test instances form matching faster.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…