• IEnumerable和IQueryable的区别以及背后的ExpressionTree表达式树


    关于IEnumerable和IQueryable的区别,这事还要从泛型委托Func<T>说起。来看一个简单的泛型委托例子:

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Func<int, bool> f = i => i > 5;
    
                Console.WriteLine(f(3));
    
                Console.WriteLine(f(10));
    
                Console.ReadKey();
    
            }
    
        }

    Func<T>是"语法糖",实际上,编译器在内部会生成一个临时方法,再执行该方法。等同于如下:

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Func<int, bool> f = DoSth;
    
                Console.WriteLine(f(3));
    
                Console.ReadKey();
    
            }
    
            static bool DoSth(int i)
    
            {
    
                return i > 5;
    
            }
    
        }
    

    以上,.NET内部运作的路径是:编写C#代码→编译器编译成中间语言IL→运行时JIT编译成本地语言执行

    ■ 使用表达式树 Expression Tree

     

    可是,有时候我们希望在运行时执行代码,该怎么办呢?

    .NET为我们提供了Expression Tree,允许我们在运行时执行代码。

    比如以上Func<int, bool> f = i => i > 5;这个表达式,Expression Tree这样理解这个表达式:

    ○ f是Expression<Func<int, bool>>类型,级Expression<TDelegate>类型
    ○ =>被理解成BinaryExpression类型
    ○ =>左右两边的i被理解成ParameterExpression
    ○ =>右边的5被理解成ConstantExpression

    于是,如果我们用Expression Tree,在运行时执行代码,可以按如下写:

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                //Func<int, bool> f = i => i > 5;
    
                ParameterExpression iParam = Expression.Parameter(typeof (int), "i");
    
                ConstantExpression constExp = Expression.Constant(5, typeof (int));
    
                BinaryExpression greaterThan = Expression.GreaterThan(iParam, constExp);
    
                Expression<Func<int, bool>> f = Expression.Lambda<Func<int, bool>>(greaterThan, iParam);
    
                Func<int, bool> myDele = f.Compile();
    
                Console.WriteLine(myDele(3));
    
                Console.WriteLine(myDele(10));
    
                Console.ReadKey();
    
            }
    
        }
    

    ■ IQueryable和IEnumerable的区别   

    现在,可以看一个IEnumerable的例子了:

       class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                int[] intArr = new[] {1, 2, 3, 6, 8};
    
                IEnumerable<int> result = Enumerable.Where(intArr, i => i > 5);
    
                foreach (var item in result)
    
                {
    
                    Console.WriteLine(item);
    
                }
    
                Console.ReadKey();
    
            }
    
        }
    

    来看一下Enumerable,实现了IEnumerable接口,它的定义:

    1

    再来看Queryable,实现了IQueryable接口,它的定义:

    2

    发现,Enumerable和Queryable很多方法同名,但参数接收的参数类型是不一样的,Enumerable接收的参数类型是委托Func<TDelegate>,Querable接收的参数类型是Expression<Func<TDelegate>>,其类型是Expression Tree,是表达式树。

    所以,有关IEnumerable<T>的表达式是在编译期确定的,有关IQueryable<T>的表达式是在运行时确定的。

    ■ 在Entity Framework应用实例中体会IQueryable<T>

     

    首先在控制台应用程序中应用Entity Framework组件。

    创建有关Entity Framework的上下文类,类,初始数据:

        public class Person
    
        {
    
            [Key]
    
            public int ID { get; set; }
    
            public string Name { get; set; }
    
            public int Age { get; set; }
    
        }
    
        public class MyContext : DbContext
    
        {
    
            public MyContext() : base("myConn")
    
            {
    
                Database.SetInitializer(new DbInirializer());
    
            }
    
            public DbSet<Person> People { get; set; }
    
        }
    
        public class DbInirializer : CreateDatabaseIfNotExists<MyContext>
    
        {
    
            protected override void Seed(MyContext context)
    
            {
    
                IList<Person> people = new List<Person>();
    
                people.Add(new Person(){Name = "张三",Age = 21});
    
                people.Add(new Person() { Name = "李四", Age = 22 });
    
                people.Add(new Person() { Name = "赵五", Age = 23 });
    
                foreach (var item in people)
    
                {
    
                    context.People.Add(item);
    
                }
    
                base.Seed(context);
    
            }
    
        }
    

    以上,如果转到DbSet的定义,我们可以看到DbSet实现了IQueryable接口。  

    配置连接字符串。

    <configuration>
    
      <configSections>
    
        <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    
        <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    
      </configSections>
    
        <connectionStrings>
    
        <add name="myConn"
    
           connectionString="Data Source=.;User=yourusename;Password=yourpassword;Initial Catalog=MyTest;Integrated Security=True"
    
           providerName="System.Data.SqlClient"/>
    
      </connectionStrings>
    
      <startup>
    
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    
      </startup>
    
      <entityFramework>
    
        <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    
        <providers>
    
          <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    
        </providers>
    
      </entityFramework>
    
    </configuration>    

    在主程序中:

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                using (var context = new MyContext())
    
                {
    
                    foreach (var item in context.People)
    
                    {
    
                        Console.WriteLine(item.Name);
    
                    }
    
                }
    
                Console.ReadKey();
    
            }
    
        }

    现在来体会IQueryable<T>的一些特性。

    我们知道,DbSet实现了IQuerayble接口,于是上下文的的People属性类型是IQueryable<Person>。

    通过,

    IQueryable<Person> people = context.People;

    得到的people是表达式,是sql语句,现在尝试打印不同情况下的people表达式。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                using (var context = new MyContext())
    
                {
    
                    IQueryable<Person> people = context.People;
    
                    var r = new Random();
    
                    Func<bool> rBool = () => r.Next()%2 == 0;
    
                    Console.WriteLine(people);
    
                    if (rBool())
    
                    {
    
                        people = people.Where(p => p.Age > 21);
    
                        Console.WriteLine(people);
    
                    }
    
                    else
    
                    {
    
                        people = people.OrderBy(p => p.Age);
    
                        Console.WriteLine(people);
    
                    }
    
                }
    
                Console.ReadKey();
    
            }
    
        }
    


    4

    由此可以看出:IQueryable呈现给我们的是表达式而不是集合,通过这个表达式可以按需加载满足条件的数据。

  • 相关阅读:
    2018.5.5-6 GDCPC2018广东省赛 6/10 Rank12 Au
    网络流24题总结
    BZOJ4259 残缺的字符串 FFT
    [转]CodePlus 2018 3月赛 博弈论与概率统计
    Aiiage Camp Day6 J Sort
    Daily Scrum7
    Daily Scrum6
    Daily Scrum5
    Daily Scrum4
    Daily Scrum3
  • 原文地址:https://www.cnblogs.com/darrenji/p/4383488.html
Copyright © 2020-2023  润新知