美文网首页
C# :IQueryable & IEnumerable

C# :IQueryable & IEnumerable

作者: 虎鲸1225 | 来源:发表于2024-05-29 23:11 被阅读0次
    image.png

    1. IEnumerable

    namespace System.Collections:
    
    public interface IEnumerable
    {
      public IEnumerator GetEnumerator ();
    }
    
    public interface IEnumerator
    {
        pubilc object Current { get; }
        public bool MoveNext ();
        public void Reset ();
    }
    

    IEnumerable 只有一个方法 GetEnumerator(), 既实现IEnumerable的所有泛型集合,都具备可枚举(IEnumerator)的能力

    IEnumerator 只有一个属性 Current,和两个方法 MoveNext() \ Reset()

    通过 MoveNext()Current 可以不停地移动 enumerator 的位置并返回当前的元素。

    Reset() 会将 enumerator 设置到初始位置,既第一个元素之前

    2. IQueryable

    namespace System.Linq:
    
    public interface IQueryable<out T> : System.Collections.Generic.IEnumerable<out T>, System.Linq.IQueryable {}
    
    public interface IQueryable : System.Collections.IEnumerable
    {
        // 表达式树返回结果的元素类型
        public Type ElementType { get; }
    
        // 获取IQueryable实例的表达式树
        public System.Linq.Expressions.Expression Expression { get; }
    
        public System.Linq.IQueryProvider Provider { get; }
    }
    
    // 定义用于创建和执行IQueryable对象所描述的查询的方法
    public interface IQueryProvider
    {
       public System.Linq.IQueryable CreateQuery(System.Linq.Expressions.Expression expression);
    
       public System.Linq.IQueryable<TElement> CreateQuery<TElement> (System.Linq.Expressions.Expression expression);
    
       public object? Execute(System.Linq.Expressions.Expression expression);
    
       public TResult Execute<TResult> (System.Linq.Expressions.Expression expression);
    }
    

    IQueryable 继承 IEnumerable,所以 IQueryable 具备可枚举的能力。

    IQueryable 中的 Expession / Provider 则用来实现LINQ to SQL,具体可以看接下来的2节详细解释。

    3. LINQ to SQL

    LINQ to SQL是.Net Framework v3.5的组件,是能够提供将关系数据作为对象管理的运行时基础结构。

    以往,编程语言通过 API 访问数据库数据的时候,需要将查询语句转为文本字符串。LINQ to SQL则会将对象模型中的语言集成查询转换为SQL,并发给数据库执行。当返回结果时,LINQ to SQL会再转换回可以用编程语言处理额对象。

    所以,当拥有一个查询的时候,并不意味着查询已经执行:

    var q = from c in dbContext.Customers Where c.City == ":London" select c;
    

    命令对象会保留描述查询的字符串,IQueryable 对象的 Expression。命令对象的 ExecuteReader() 方法执行后,以 DataReader 形式返回结果。IQueryable 对象通过 GetEnumerator() 方法返回 IEnumerator<T> 结果。

    如下 foreach 会执行两次 query,这种行为称为延迟执行

    var q =
       from c in db.Customers
       where c.City == "London"
       select c;
    // Execute first time
    foreach (Customer c in q)
       Console.WriteLine(c.CompanyName);
    // Execute second time
    foreach (Customer c in q)
       Console.WriteLine(c.CompanyName);
    

    如果提前将结果转为任意标准的集合类,可以避免重复执行。

    var q =
       from c in db.Customers
       where c.City == "London"
       select c;
    // Execute once using ToList() or ToArray()
    var list = q.ToList();
    foreach (Customer c in list)
       Console.WriteLine(c.CompanyName);
    foreach (Customer c in list)
       Console.WriteLine(c.CompanyName);
    

    更多资料:LINQ:.NET Language-Integrated查询

    4. IEnumerable & IQueryable

    4.1 Expression

    针对 IEnumerable<T>所设计的扩展方法都将表达式视为委托,而针对 IQueryable<T> 的那些扩展方法用的则是表达式树(expression tree)

    IQueryable 会解析表达式树,并把这棵树表示的逻辑转为 provider 能够操作的格式,将其放在离数据最近的地方去执行。即传输数据往往会少于 IEnumerable,总体性能更好。

    借鉴 《Effective C#》中的例子,如下两种写法,返回的结果相同,但是工作方式却不同:

    // 1. use IQueryable
    var q = from c in dbContext.Customers Where c.City == ":London" select c;
    var finalAnswer = from c in q order by c.Name select c;
    
    // 2. use Enumerable
    var q = (from c in dbContext.Customers where c.City == "London" select c).AsEnumerable();
    var finaAnswer = from c in q order by c.Name select c;
    

    方法1,采用的是 IQueryable<T>内置的 LINQ to SQL,q的查询语句,会和 第二行的组合起来,即只需要向数据库发送一次调用,where和orderby会在同一次sql查询操作里完成

    方法2,则是把数据库对象从 IQueryable 强制转为了 IEnumerable形式的标准可枚举对象,即先向数据库发送查询请求,获得所有的数据后,放在本地进行排序操作

    假如每种方法第二行还有一次 where 筛选一部分数据,那么方法1会组合2次 where,数据库只会返回最终目标数据集。而方法2会先从数据把所有第一次 where 得到的数据传到本地,之后在本地再次筛选,无疑增加了网络数据传输量。

    4.2 Provider

    IQueryable 的 Provider 未必能支持每一种查询方式,只能支持某些固定的运算符、方法,所以一旦查询操作里面调用除此之外的方法,那么就有可能把序列当成 IEnumerable 来查询,而非 IQueryable,否则会抛出异常。

    再上 《Effective c#》例子:

    private bool isValidProduct(Product p) => p.ProductName.LastIndexOf('C') == 0;
    
    // 1. 转为 Enumerable 执行
    var q1 = from p in dbContext.Products.AsEnumerable() where isValidProduct(p) select p;
    
    // 2. 直接执行 IsValidProduct
    var q2 = from p in dbContext.Products where isValidProduct(p) select p;
    

    方法1可以正常运行,只是在 AsEnumerable() 之后,查询必须在本地执行,where 字句内的逻辑由LINQ to Objects处理。

    方法2则会抛出异常,因为IQueryProvider会把查询操作转为T-SQL,交由远端执行。

    相关文章

      网友评论

          本文标题:C# :IQueryable & IEnumerable

          本文链接:https://www.haomeiwen.com/subject/fjgkqjtx.html