Expression>和Func

Expression> vs Func

Posted by Zeusro on May 3, 2015
👈🏻 Select language

以前用EF的时候,由于where的时候有Expression<Func>和Func两种查询条件,误用了Func那个重载,后来还想通过func创建查询来着,不过失败了,导致了全表查询,真是无语.国内的人答的比较言简意赅(其实我觉得讲的不好).还是老外讲的明白点.

翻译过来吧,就是说Func是方法的委托,而Expression<Func>是拉姆达表达式树.这个树状结构描述了各种各样恶心的参数(如下图所示).我们可以用Expression.Compile做成一个委托或者编译成sql(EF).

1
Expression<Func<int>> myExpression = () => 10;

其实吧, 多用一下你就知道了.Func用的还蛮多的,当时就是用来运行泛化的方法的,而Expression<Func>用在动态查询拼接的时候比较多,比如 (And和or,拼接多条表达式树).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.And));
    }

    private static Expression<Func<T, bool>> Compose<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, Func<Expression, Expression, Expression> merge)
    {
        Expression expression = new ParameterRebinder(second.Parameters[0], first.Parameters[0]).Visit(second.Body);
        return Expression.Lambda<Func<T, bool>>(merge(first.Body, expression), first.Parameters);
    }

    public static Expression<Func<T, bool>> False<T>()
    {
        return item => false;
    }

    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> predicate)
    {
        return Expression.Lambda<Func<T, bool>>(Expression.Not(predicate.Body), predicate.Parameters);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.Or));
    }

    public static Expression<Func<T, bool>> True<T>()
    {
        return item => true;
    }

    private sealed class ParameterRebinder : ExpressionVisitor
    {
        private readonly ParameterExpression m_From;
        private readonly ParameterExpression m_To;

        public ParameterRebinder(ParameterExpression from, ParameterExpression to)
        {
            this.m_From = from;
            this.m_To = to;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == this.m_From)
            {
                node = this.m_To;
            }
            return base.VisitParameter(node);
        }
    }
}

表达式树恶心的地方,我写一个orderby给你看看.

1
2
3
4
5
6
7
8
9
10
11
12
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
    string command = desc ? "OrderByDescending" : "OrderBy";
    var type = typeof(TEntity);//实体的类型
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "o");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
                                  source.Expression, Expression.Quote(orderByExpression));
    return source.Provider.CreateQuery<TEntity>(resultExpression);
}

动态linq是需要反射的.而且这种写法不利于调试.因为你特么完全不知道生成的什么鬼,除非你对这玩意真的很熟.好吧,你赢了.

参考链接:

  1. [Why would you use Expression<Func> rather than Func?](https://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct)
  2. Entity Framework - Func引起的数据库全表查询
  3. 通过已有Func构造Expression表达式问题

When I was using EF (Entity Framework), there were two types of query conditions for where: Expression<Func> and Func. I mistakenly used the Func overload, and later tried to create queries through func, but it failed, resulting in a full table query. It was really frustrating. People in China answered quite concisely (I actually think they didn't explain it well). Foreigners explained it more clearly.

To translate: Func is a method delegate, while Expression<Func> is a lambda expression tree. This tree structure describes various parameters (as shown in the figure below). We can use Expression.Compile to create a delegate or compile it into SQL (EF).

1
Expression<Func<int>> myExpression = () => 10;

Actually, you’ll understand once you use it more. Func is used quite a lot, mainly for running generalized methods, while Expression<Func> is used more for dynamic query concatenation, such as (And and Or, concatenating multiple expression trees).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.And));
    }

    private static Expression<Func<T, bool>> Compose<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, Func<Expression, Expression, Expression> merge)
    {
        Expression expression = new ParameterRebinder(second.Parameters[0], first.Parameters[0]).Visit(second.Body);
        return Expression.Lambda<Func<T, bool>>(merge(first.Body, expression), first.Parameters);
    }

    public static Expression<Func<T, bool>> False<T>()
    {
        return item => false;
    }

    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> predicate)
    {
        return Expression.Lambda<Func<T, bool>>(Expression.Not(predicate.Body), predicate.Parameters);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.Or));
    }

    public static Expression<Func<T, bool>> True<T>()
    {
        return item => true;
    }

    private sealed class ParameterRebinder : ExpressionVisitor
    {
        private readonly ParameterExpression m_From;
        private readonly ParameterExpression m_To;

        public ParameterRebinder(ParameterExpression from, ParameterExpression to)
        {
            this.m_From = from;
            this.m_To = to;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == this.m_From)
            {
                node = this.m_To;
            }
            return base.VisitParameter(node);
        }
    }
}

The annoying part about expression trees, let me show you an orderby example:

1
2
3
4
5
6
7
8
9
10
11
12
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
    string command = desc ? "OrderByDescending" : "OrderBy";
    var type = typeof(TEntity);//Entity type
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "o");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
                                  source.Expression, Expression.Quote(orderByExpression));
    return source.Provider.CreateQuery<TEntity>(resultExpression);
}

Dynamic LINQ requires reflection. And this writing style is not conducive to debugging. Because you have no idea what the hell is being generated, unless you’re really familiar with this stuff. Well, you win.

  1. [Why would you use Expression<Func> rather than Func?](https://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct)
  2. Entity Framework - Func引起的数据库全表查询
  3. 通过已有Func构造Expression表达式问题

Когда я использовал EF (Entity Framework), для where было два типа условий запроса: Expression<Func> и Func. Я ошибочно использовал перегрузку Func, а затем попытался создать запросы через func, но это не удалось, что привело к полному запросу таблицы. Это было действительно неприятно. Люди в Китае отвечали довольно кратко (на самом деле я думаю, что они объяснили это не очень хорошо). Иностранцы объяснили это более ясно.

Переводя: Func - это делегат метода, а Expression<Func> - это дерево лямбда-выражений. Эта древовидная структура описывает различные параметры (как показано на рисунке ниже). Мы можем использовать Expression.Compile для создания делегата или компиляции его в SQL (EF).

1
Expression<Func<int>> myExpression = () => 10;

На самом деле, вы поймете, как только будете использовать это больше. Func используется довольно часто, в основном для запуска обобщенных методов, в то время как Expression<Func> используется больше для динамической конкатенации запросов, например (And и Or, конкатенация нескольких деревьев выражений).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.And));
    }

    private static Expression<Func<T, bool>> Compose<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, Func<Expression, Expression, Expression> merge)
    {
        Expression expression = new ParameterRebinder(second.Parameters[0], first.Parameters[0]).Visit(second.Body);
        return Expression.Lambda<Func<T, bool>>(merge(first.Body, expression), first.Parameters);
    }

    public static Expression<Func<T, bool>> False<T>()
    {
        return item => false;
    }

    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> predicate)
    {
        return Expression.Lambda<Func<T, bool>>(Expression.Not(predicate.Body), predicate.Parameters);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose<T>(second, new Func<Expression, Expression, Expression>(Expression.Or));
    }

    public static Expression<Func<T, bool>> True<T>()
    {
        return item => true;
    }

    private sealed class ParameterRebinder : ExpressionVisitor
    {
        private readonly ParameterExpression m_From;
        private readonly ParameterExpression m_To;

        public ParameterRebinder(ParameterExpression from, ParameterExpression to)
        {
            this.m_From = from;
            this.m_To = to;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == this.m_From)
            {
                node = this.m_To;
            }
            return base.VisitParameter(node);
        }
    }
}

Раздражающая часть деревьев выражений, позвольте показать вам пример orderby:

1
2
3
4
5
6
7
8
9
10
11
12
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
    string command = desc ? "OrderByDescending" : "OrderBy";
    var type = typeof(TEntity);//Тип сущности
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "o");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
                                  source.Expression, Expression.Quote(orderByExpression));
    return source.Provider.CreateQuery<TEntity>(resultExpression);
}

Динамический LINQ требует рефлексии. И этот стиль написания не способствует отладке. Потому что вы понятия не имеете, что черт возьми генерируется, если только вы не очень хорошо знакомы с этим. Ну, вы выиграли.

Ссылки:

  1. [Why would you use Expression<Func> rather than Func?](https://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct)
  2. Entity Framework - Func引起的数据库全表查询
  3. 通过已有Func构造Expression表达式问题