Create Dynamic Func from Object

前端 未结 2 444
忘了有多久
忘了有多久 2021-01-01 06:18

I have a criteria object in which I was to turn each property into a func, if it\'s value isn\'t null.

public class TestClassCriteria
{
    public bool? Colu         


        
相关标签:
2条回答
  • 2021-01-01 07:07

    Your current expression is comparing the property names, but I think you want to be comparing the property values:

    var funcs = new List<Func<dynamic, bool>>();
    
    foreach (var property in criteria.GetType().GetProperties())
    {
        funcs.Add(x => x.GetType().GetProperty(property.Name).GetValue(x, null) == property.GetValue(criteria, null));
    }
    

    However, are you sure you need to do this? There would probably be a better way to refactor your code so that you don't need to use reflection to compare properties that happen to have the same name on two unrelated objects.

    0 讨论(0)
  • 2021-01-01 07:11

    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.

    0 讨论(0)
提交回复
热议问题