02020509 EF Core高级09-生成静态表达式树、动态创建表达式树、简化表达式树

02020509 EF Core高级09-生成静态表达式树、动态创建表达式树、简化表达式树

1. 更简单的创建表达式树(视频3-44)

1.1 动态创建表达式树
1、通过代码来动态构造表达式树要求开发者精通表达式树的结构,甚至还需要了解CLR底层的机制。不过可以用ExpressionTreeToString来简化动态构造表达式树的代码。
2、可以用ExpressionTreeToString的ToString("Factory methods", "C#")输出类似于工厂方法生成这个表达式树的代码。
3、输出的所有代码都是对于工厂方法的调用,不过调用工厂方法的时候都省略了Expression类。手动添加Expression或者用using static
4、可能需要微调生成的代码。
1.2 动态创建表达式树示例
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace TreeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请选择输出筛选方式:1 → 大于;2 → 小于");

            string str = Console.ReadLine();

            ParameterExpression paramExpB =  Expression.Parameter(typeof(Book), "b"); // 创建参数节点

            ConstantExpression constExp5 = Expression.Constant(5 * 1.0, typeof(double)); // 创建常量节点

            MemberExpression memExpPrice = Expression.MakeMemberAccess(paramExpB, typeof(Book).GetProperty("Price")); // 创建属性节点

            BinaryExpression binExpCompare;

            if (str == "1") // 根据用户选择来生成大于比较或者小于比较
            {
                binExpCompare = Expression.GreaterThan(memExpPrice, constExp5);
            }
            else
            {
                binExpCompare = Expression.LessThan(memExpPrice, constExp5);
            }

            Expression<Func<Book, bool>> lamExpRoot = Expression.Lambda<Func<Book, bool>>(binExpCompare, paramExpB); // 创建Lambda节点

            using (MyDbContext ctx = new MyDbContext())
            {
                ctx.Books.Where(lamExpRoot).ToArray();
            }

            Console.WriteLine("动态创建成功!");
        }
    }
}

控制台输出:
请选择输出筛选方式:1 → 大于;2 → 小于
2 // 输入
动态创建成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 输入1:查看SQL
SELECT [t].[Id], [t].[AuthorName], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] > 5.0E0

// 输入2:查看SQL
SELECT [t].[Id], [t].[AuthorName], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] < 5.0E0
1.3 给实体类增加属性
// Book.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TreeDemo
{
    class Book
    {
        public long Id { get; set; }
        public string Title { get; set; }
        public DateTime PubTime { get; set; }
        public double Price { get; set; }
        public string AuthorName { get; set; }
        public int Age01 { get; set; }
        public int Age02 { get; set; }
        public string Name { get; set; }
    }
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 迁移数据库
PM> add-migration init02
PM> update-database
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 查看数据表内容
Id	Title	PubTime	Price	AuthorName	Age01	Age02	Name
3	零基础趣学C语言	2019-03-01 00:00:00.0000000	59.8	杨中科	0	0	NULL
4	算法第4版	2012-10-01 00:00:00.0000000	99	Robert Sedgewick	0	0	NULL
5	数学之美	2020-05-01 00:00:00.0000000	69	吴军	0	0	NULL
6	程序员的SQL经典	2008-09-01 00:00:00.0000000	52	杨中科	0	0	NULL
7	文明之光	2017-03-01 00:00:00.0000000	246	吴军	0	0	NULL

2. 使用类似工厂方法生成表达式树

2.1 类似工厂方法生成表达式树示例
// 查看e1
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace TreeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<Book, bool>> e1 = b => b.Price > 5;
            Expression<Func<Book, Book, double>> e2 = (b1, b2) => b1.Price + b2.Price;

            Console.WriteLine(e1.ToString("Factory methods", "C#")); // 类似工厂方法生成的表达式树

            Console.WriteLine("动态创建成功!");
        }
    }
}

控制台输出:
// using static System.Linq.Expressions.Expression
var b = Parameter(
    typeof(Book),
    "b"
);

Lambda(
    GreaterThan(
        MakeMemberAccess(b,
            typeof(Book).GetProperty("Price")
        ),
        Constant(5)
    ),
    b
)
动态创建成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 查看e2
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace TreeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<Book, bool>> e1 = b => b.Price > 5;
            Expression<Func<Book, Book, double>> e2 = (b1, b2) => b1.Price + b2.Price;

            Console.WriteLine(e2.ToString("Factory methods", "C#")); // 类似工厂方法生成的表达式树

            Console.WriteLine("动态创建成功!");
        }
    }
}

控制台输出:
// using static System.Linq.Expressions.Expression
var b1 = Parameter(
    typeof(Book),
    "b1"
);
var b2 = Parameter(
    typeof(Book),
    "b2"
);

Lambda(
    Add(
        MakeMemberAccess(b1,
            typeof(Book).GetProperty("Price")
        ),
        MakeMemberAccess(b2,
            typeof(Book).GetProperty("Price")
        )
    ),
    b1, b2
)
动态创建成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
说明:在两个输出内容里面都有:// using static System.Linq.Expressions.Expression
2.2 static引用生成e1对应的SQL
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var b = Parameter( // 使用staitc引用,不用写为Expression.Parameter,而是直接用Parameter即可。
                typeof(Book),
                "b"
            );

            var exp = Lambda<Func<Book, bool>>( // 泛型
                GreaterThan(
                    MakeMemberAccess(b,
                        typeof(Book).GetProperty("Price")
                    ),
                    Constant(5 * 1.0) // 注意为double类型
                ),
                b
            );

            using (MyDbContext ctx = new MyDbContext())
            {
                ctx.Books.Where(exp).ToArray();
            }

            Console.WriteLine("创建成功!");
        }
    }
}

控制台输出:
创建成功!

// 查看SQL
SELECT [t].[Id], [t].[Age01], [t].[Age02], [t].[AuthorName], [t].[Name], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] > 5.0E0
2.3 通过if判断动态生成SQL
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("请选择输出筛选方式:1 → 大于;2 → 小于");
            string str = Console.ReadLine();

            var b = Parameter( // 使用staitc引用,不用写为Expression.Parameter,而是直接用Parameter即可。
                typeof(Book),
                "b"
            );

            BinaryExpression binExpCompare;

            if (str == "1") // 根据用户选择来生成大于比较或者小于比较
            {
                binExpCompare = GreaterThan(
                    MakeMemberAccess(b,
                        typeof(Book).GetProperty("Price")
                    ),
                    Constant(5 * 1.0) // 注意为double类型
                );
            }
            else
            {
                binExpCompare = LessThan(
                    MakeMemberAccess(b,
                        typeof(Book).GetProperty("Price")
                    ),
                    Constant(5 * 1.0) // 注意为double类型
                );
            }


            var exp = Lambda<Func<Book, bool>>(binExpCompare, b);

            using (MyDbContext ctx = new MyDbContext())
            {
                ctx.Books.Where(exp).ToArray();
            }

            Console.WriteLine("动态创建成功!");
        }
    }
}

控制台输出:
请选择输出筛选方式:1 → 大于;2 → 小于
2 // 输入
动态创建成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 输入1:查看SQL
SELECT [t].[Id], [t].[Age01], [t].[Age02], [t].[AuthorName], [t].[Name], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] > 5.0E0

// 输入2:查看SQL
SELECT [t].[Id], [t].[Age01], [t].[Age02], [t].[AuthorName], [t].[Name], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] < 5.0E0
2.4 继续简化
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("请选择输出筛选方式:1 → 大于;2 → 小于");
            string str = Console.ReadLine();

            var b = Parameter( // 使用staitc引用,不用写为Expression.Parameter,而是直接用Parameter即可。
                typeof(Book),
                "b"
            );

            var expPrice = MakeMemberAccess(b, typeof(Book).GetProperty("Price"));

            var const5 = Constant(5 * 1.0); // 注意为double类型


            BinaryExpression binExpCompare;

            if (str == "1") // 根据用户选择来生成大于比较或者小于比较
            {
                binExpCompare = GreaterThan(expPrice, const5);
            }
            else
            {
                binExpCompare = LessThan(expPrice, const5);
            }


            var exp = Lambda<Func<Book, bool>>(binExpCompare, b);

            using (MyDbContext ctx = new MyDbContext())
            {
                ctx.Books.Where(exp).ToArray();
            }

            Console.WriteLine("动态创建成功!");
        }
    }
}

控制台输出:
请选择输出筛选方式:1 → 大于;2 → 小于
2 // 输入
动态创建成功!
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 输入1:查看SQL
SELECT [t].[Id], [t].[Age01], [t].[Age02], [t].[AuthorName], [t].[Name], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] > 5.0E0

// 输入2:查看SQL
SELECT [t].[Id], [t].[Age01], [t].[Age02], [t].[AuthorName], [t].[Name], [t].[Price], [t].[PubTime], [t].[Title]
      FROM [T_Books] AS [t]
      WHERE [t].[Price] < 5.0E0

3. 让动态表达式树“动态”起来(视频3-35)

3.1 动态创建表达式树原理
1、动态构建表达式树最有价值的地方就是运行时根据条件的不同生成不同的表达式树。
2、编写:static IEnumerable<Book> QueryBooks(string propName,object value)
其中propName为要查询的属性的名字,value为待比较的值。
3、先编写
Expression<Func<Book, bool>> expr1 = b => b.Price == 5;
Expression<Func<Book, bool>> expr2 = b => b.Title == "零基础趣学C语言";
查看代码再写代码 e1.ToString("Factory methods", "C#")
4、判断是否基本数据类型:if(propType.IsPrimitive)
5、测试调用:QueryBooks("Price", 18.0);QueryBooks("AuthorName", "杨中科");
3.2 先观察静态长啥样
// 静态观察Price
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.Price == 5;
            	Console.WriteLine(e1.ToString("Factory Methods", "C#"));
            }
        }
    }
}

控制台输出:
// using static System.Linq.Expressions.Expression

var e = Parameter(
    typeof(Book),
    "e"
);

Lambda(
    Equal( // @1 double类型比较
        MakeMemberAccess(e,
            typeof(Book).GetProperty("Price")
        ),
        Constant(5) 
    ),
    e
)
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 静态观察AuthorName
using ExpressionTreeToString;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e2 = e => e.AuthorName == "杨中科";
                Console.WriteLine(e2.ToString("Factory Methods", "C#"));
            }
        }
    }
}

控制台输出:
// using static System.Linq.Expressions.Expression

var e = Parameter(
    typeof(Book),
    "e"
);

Lambda(
    MakeBinary(ExpressionType.Equal, // @2 字符串类型比较
        MakeMemberAccess(e,
            typeof(Book).GetProperty("AuthorName")
        ),
        Constant("杨中科"), false,
        typeof(string).GetMethod("op_Equality") 
    ),
    e
)

说明:
1. 在C#中,无论是double类型还是string类型,都可以用==来判断。
2. 在表达式树中,我们观察它们并不一样。可以在@1处和@2处观察出来。
3. 原始数据类型(如:double、int)在表达式树中用equal来比较,而字符串等类型用MakeBinary来比较。
3.3 当前数据库信息
// 查看数据表内容
Id	Title	PubTime	Price	AuthorName	Age01	Age02	Name
3	零基础趣学C语言	2019-03-01 00:00:00.0000000	59.8	杨中科	0	0	NULL
4	算法第4版	2012-10-01 00:00:00.0000000	99	Robert Sedgewick	0	0	NULL
5	数学之美	2020-05-01 00:00:00.0000000	69	吴军	0	0	NULL
6	程序员的SQL经典	2008-09-01 00:00:00.0000000	52	杨中科	0	0	NULL
7	文明之光	2017-03-01 00:00:00.0000000	246	吴军	0	0	NULL
3.4 使用静态表达式树查询示例
// Book.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TreeDemo
{
    class Book
    {
        public long Id { get; set; }
        public string Title { get; set; }
        public DateTime PubTime { get; set; }
        public double Price { get; set; }
        public string AuthorName { get; set; }
        public int Age01 { get; set; }
        public int Age02 { get; set; }
        public string Name { get; set; }

        // 重写ToString方法便于输出
        public override string ToString()
        {
            return $"Id = {Id},title = {Title},PubTime = {PubTime}, Price = {Price}, AuthorNmae = {AuthorName}";
        }
    }
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                var books = StaticQueryBooks("Price", 69);
                foreach (var item in books)
                {
                    Console.WriteLine(item);
                }
            }
        }

        // 静态表达式树查询方法
        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList(); // 注意避免延迟执行的问题,用ToList或者ToArray。
            }
        }
    }
}

控制台输出:
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科
3.5 使用动态表达式树查询示例
  • 原始数据类型(如:double、int)在表达式树中用equal来比较,而字符串等类型用MakeBinary来比较。
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                // var books = StaticQueryBooks("Price", 69);
                var books01 = DynamicQueryBooks("Price", 69 * 1.0); // 注意属性值为double类型
                foreach (var item in books01)
                {
                    Console.WriteLine(item);
                }

                Console.WriteLine("********************");

                var books02 = DynamicQueryBooks("AuthorName", "杨中科"); // 注意属性值为string类型
                foreach (var item in books02)
                {
                    Console.WriteLine(item);
                }
            }
        }

        // 静态表达式树查询方法
        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList(); // 注意避免延迟执行的问题,用ToList或者ToArray。
            }
        }

        static IEnumerable<Book> DynamicQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1;

                Type valueType = value.GetType(); // 获取属性的类型
                if(valueType.IsPrimitive) // 判断属性的类型是否为原始数据类型
                {
                    var e = Parameter(typeof(Book), "e");
                    // 参考本章3.2中原始类型写法,需要替换传入的propertyName、value
                    e1 = Lambda<Func<Book, bool>>(Equal(MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), Constant(value)), e);
                }
                else
                {
                    var e = Parameter(typeof(Book), "e");
                    // 参考本章3.2中字符串类型的写法
                    e1 = Lambda<Func<Book, bool>>(MakeBinary(ExpressionType.Equal, MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), 
                        Constant(value), false, typeof(string).GetMethod("op_Equality")), e);
                }
                return ctx.Books.Where(e1).ToList();
            }
        }
    }
}

控制台输出:
Id = 5,title = 数学之美,PubTime = 2020/5/1 0:00:00, Price = 69, AuthorNmae = 吴军
********************
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 提取var e = Parameter(typeof(Book), "e");并简化程序
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                // var books = StaticQueryBooks("Price", 69);
                var books01 = DynamicQueryBooks("Price", 69 * 1.0); // 注意值为double类型
                foreach (var item in books01)
                {
                    Console.WriteLine(item);
                }

                Console.WriteLine("********************");

                var books02 = DynamicQueryBooks("AuthorName", "杨中科"); // 注意值为double类型
                foreach (var item in books02)
                {
                    Console.WriteLine(item);
                }
            }
        }

        // 静态表达式树查询方法
        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList(); // 注意避免延迟执行的问题,用ToList或者ToArray。
            }
        }

        static IEnumerable<Book> DynamicQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1;

                var e = Parameter(typeof(Book), "e");

                Type valueType = value.GetType(); // 获取属性的类型
                if(valueType.IsPrimitive) // 判断属性的类型是否为原始数据类型
                {

                    // 参考本章3.2中原始类型写法,需要替换传入的propertyName、value
                    e1 = Lambda<Func<Book, bool>>(Equal(MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), Constant(value)), e);
                }
                else
                {
                    // 参考本章3.2中字符串类型的写法
                    e1 = Lambda<Func<Book, bool>>(MakeBinary(ExpressionType.Equal, MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), 
                        Constant(value), false, typeof(string).GetMethod("op_Equality")), e);
                }
                return ctx.Books.Where(e1).ToList();
            }
        }
    }
}

控制台输出:
Id = 5,title = 数学之美,PubTime = 2020/5/1 0:00:00, Price = 69, AuthorNmae = 吴军
********************
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科
3.6 通过反射智能转换类型
  • 在本章3.5中,Price属性为double类型,但是数据库中我们存入的为69是int类型,这样每次都需要转换。我们可以通过反射来实现智能转换
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                // var books = StaticQueryBooks("Price", 69);
                var books01 = DynamicQueryBooks("Price", 69); // @3
                foreach (var item in books01)
                {
                    Console.WriteLine(item);
                }

                Console.WriteLine("********************");

                var books02 = DynamicQueryBooks("AuthorName", "杨中科"); // 注意值为double类型
                foreach (var item in books02)
                {
                    Console.WriteLine(item);
                }
            }
        }

        // 静态表达式树查询方法
        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList(); // 注意避免延迟执行的问题,用ToList或者ToArray。
            }
        }

        static IEnumerable<Book> DynamicQueryBooks(string propertyName, object value) // 属性名、属性值作为参数
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1;

                var e = Parameter(typeof(Book), "e");

                Type valueType = typeof(Book).GetProperty(propertyName).PropertyType; // @1 通过反射获取属性,进而获取属性的类型。

                // Type valueType = value.GetType(); // 获取属性的类型
                if(valueType.IsPrimitive) // 判断属性的类型是否为原始数据类型
                {

                    // 参考本章3.2中原始类型写法,需要替换传入的propertyName、value
                    e1 = Lambda<Func<Book, bool>>(Equal(MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), 
                        Constant(System.Convert.ChangeType(value, valueType))), e); // @2
                }
                else
                {
                    // 参考本章3.2中字符串类型的写法
                    e1 = Lambda<Func<Book, bool>>(MakeBinary(ExpressionType.Equal, MakeMemberAccess(e, typeof(Book).GetProperty(propertyName)), 
                        Constant(value), false, typeof(string).GetMethod("op_Equality")), e);
                }
                return ctx.Books.Where(e1).ToList();
            }
        }
    }
}

控制台输出:
Id = 5,title = 数学之美,PubTime = 2020/5/1 0:00:00, Price = 69, AuthorNmae = 吴军
********************
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科

说明:
1. 在@1处获取属性类型。
2. 在@2处,如果直接用Covert()那么会和LINQ里面的Conver()冲突,因此用类名+方法名来使用。
3. 在@3处,此时不用输入为double类型。
3.7 继续简化(将if判断中重用的部分提取出来)
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                // var books = StaticQueryBooks("Price", 69);
                var books01 = DynamicQueryBooks("Price", 69);
                foreach (var item in books01)
                {
                    Console.WriteLine(item);
                }

                Console.WriteLine("********************");

                var books02 = DynamicQueryBooks("AuthorName", "杨中科");
                foreach (var item in books02)
                {
                    Console.WriteLine(item);
                }
            }
        }

        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList();
            }
        }

        static IEnumerable<Book> DynamicQueryBooks(string propertyName, object value) 
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1;

                var e = Parameter(typeof(Book), "e");

                Type valueType = typeof(Book).GetProperty(propertyName).PropertyType;

                // 提取重用的MakeMemberAccess
                var memAccessExp = MakeMemberAccess(e, typeof(Book).GetProperty(propertyName));

                // 提取数据类型转换部分
                var valueConstExp = Constant(System.Convert.ChangeType(value, valueType));

                if (valueType.IsPrimitive)
                {

                    e1 = Lambda<Func<Book, bool>>(Equal(memAccessExp,
                        valueConstExp), e);
                }
                else
                {
                    e1 = Lambda<Func<Book, bool>>(MakeBinary(ExpressionType.Equal, memAccessExp,
                        valueConstExp, false, typeof(string).GetMethod("op_Equality")), e);
                }
                return ctx.Books.Where(e1).ToList();
            }
        }
    }
}

控制台输出:
Id = 5,title = 数学之美,PubTime = 2020/5/1 0:00:00, Price = 69, AuthorNmae = 吴军
********************
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科
3.8 继续对Lambda简化
using ExpressionTreeToString;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression; // static引用,不用类名了,直接调用方法。

namespace TreeDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                // var books = StaticQueryBooks("Price", 69);
                var books01 = DynamicQueryBooks("Price", 69);
                foreach (var item in books01)
                {
                    Console.WriteLine(item);
                }

                Console.WriteLine("********************");

                var books02 = DynamicQueryBooks("AuthorName", "杨中科");
                foreach (var item in books02)
                {
                    Console.WriteLine(item);
                }
            }
        }

        static IEnumerable<Book> StaticQueryBooks(string propertyName, object value)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1 = e => e.AuthorName == "杨中科";
                return ctx.Books.Where(e1).ToList();
            }
        }

        static IEnumerable<Book> DynamicQueryBooks(string propertyName, object value) 
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                Expression<Func<Book, bool>> e1;

                var e = Parameter(typeof(Book), "e");

                Type valueType = typeof(Book).GetProperty(propertyName).PropertyType;

                // 提取重用的MakeMemberAccess
                var memAccessExp = MakeMemberAccess(e, typeof(Book).GetProperty(propertyName));

                // 提取数据类型转换部分
                var valueConstExp = Constant(System.Convert.ChangeType(value, valueType));

                // 提取Lambda部分
                Expression body;

                if (valueType.IsPrimitive)
                {
                    body = Equal(memAccessExp, valueConstExp);
                }
                else
                {
                    body = MakeBinary(ExpressionType.Equal, memAccessExp, valueConstExp, false, typeof(string).GetMethod("op_Equality"));
                }

                e1 = Lambda<Func<Book, bool>>(body, e);
                return ctx.Books.Where(e1).ToList();
            }
        }
    }
}

控制台输出:
控制台输出:
Id = 5,title = 数学之美,PubTime = 2020/5/1 0:00:00, Price = 69, AuthorNmae = 吴军
********************
Id = 3,title = 零基础趣学C语言,PubTime = 2019/3/1 0:00:00, Price = 59.8, AuthorNmae = 杨中科
Id = 6,title = 程序员的SQL经典,PubTime = 2008/9/1 0:00:00, Price = 52, AuthorNmae = 杨中科
3.9 构建动态表达式树总结
  • 通过先创建静态表达式树结构,帮助我们动态的来构建表达式。然后再抽取动态表达式中重复的部分来进行简化。

结尾

书籍:ASP.NET Core技术内幕与项目实战

视频:https://www.bilibili.com/video/BV1pK41137He

著:杨中科

ISBN:978-7-115-58657-5

版次:第1版

发行:人民邮电出版社

※敬请购买正版书籍,侵删请联系85863947@qq.com※

※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

posted @ 2025-10-12 18:56  qinway  阅读(14)  评论(0)    收藏  举报