Currying Expressions in C#

前端 未结 2 958
予麋鹿
予麋鹿 2021-01-05 22:20

I am trying to build up an expression tree that I can feed into Linq2SQL so that it will generate a nice clean query. My purpose is to build a filter that takes an arbitrary

相关标签:
2条回答
  • 2021-01-05 22:24

    I like using linq to build epression trees, it makes me feel uber-powerfull, so I've added this, not as a complete answer to your question, but more a an elegant way to build up expression trees...

    var query = ...;
    var search = "asdfasdf";
    var fields = new Expression<Func<MyEntity,string>>[]{ 
        x => x.Prop1, 
        x => x.Prop2, 
        x => x.Prop3 
    };
    var notFields = new Expression<Func<MyEntity,string>>[]{ 
        x => x.Prop4, 
        x => x.Prop5 };
    
    //----- 
    var paramx = Expression.Parameter(query.ElementType);
    
    //get fields to search for true
    var whereColumnEqualsx = fields
        .Select(x => Expression.Invoke(x,paramx))
        .Select(x => Expression.Equal(x,Expression.Constant(search)))
        //you could change the above to use .Contains(...) || .StartsWith(...) etc.
        //you could also make it not case sensitive by 
        //wraping 'x' with a .ToLower() expression call, 
        //and setting the search constant to 'search.ToLower()'
        .Aggregate((x,y) => Expression.And(x,y));
    
    //get fields to search for false
    var whereColumnNotEqualsx = notFields
        .Select(x => Expression.Invoke(x,paramx))
        .Select(x => Expression.NotEqual(x, Expression.Constant(search)))
        //see above for the different ways to build your 'not' expression,
        //however if you use a .Contains() you need to wrap it in an Expression.Negate(...)
        .Aggregate((x,y) => Expression.Or(x,y));
        //you can change Aggregate to use Expression.And(...) 
        //if you want the query to exclude results only if the 
        //search string is in ALL of the negated fields.
    
    var lambdax = Expression.Lambda(
        Expression.And(whereColumnEqualsx, whereColumnNotEqualsx), paramx);
    
    var wherex = Expression.Call(typeof(Queryable)
        .GetMethods()
        .Where(x => x.Name == "Where")
        .First()
        .MakeGenericMethod(query.ElementType),
        query.Expression,lambdax);
    
    //create query
    var query2 = query.Provider.CreateQuery(wherex).OfType<MyEntity>();
    
    0 讨论(0)
  • 2021-01-05 22:45

    this example might help you. I guess the best is to build the expression without lambdas:

    public class Entity
    {
        public Entity(string someField)
        {
            SomeField = someField;
        }
    
        public string SomeField { get; set;  }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var entities = new[] {new Entity("fooBar"), new Entity("barBaz"), new Entity("baz"), new Entity("foo")};
            entities.Where(BuildExpression("ar","az").Compile())
                    .ToList()
                    .ForEach(e => Console.WriteLine(e.SomeField));
            Console.ReadLine();
        }
    
        public static Expression<Func<Entity, bool>> BuildExpression(params string[] words)
        {
            var parameter = Expression.Parameter(typeof (Entity));
    
            var matchs = words.Select(word =>
                                            {
                                                var property = Expression.Property(parameter, "SomeField");
                                                var toLower = Expression.Call(property, "ToLower", new Type[] {});
                                                var contains = Expression.Call(toLower, "Contains",
                                                                                new Type[]{},
                                                                                Expression.Constant(word));
                                                return contains;
                                            }).OfType<Expression>();
    
            var body = matchs.Aggregate(Expression.Or);
    
            return Expression.Lambda<Func<Entity, bool>>(body, new[] {parameter});
        } 
    }
    

    Please let me know if I should add more information to this answer.

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