我们在应用开发中经常用到IQueryable 和 IEnumerable两个接口。如果这两个接口搞混,往往会对程序性能造成很大的影响。
本文以一个实例来区分这两个接口,说明在实际开发过程中,这两个接口如何使用,尤其是IQueryable 和接口IEnumerable中的LINQ扩展方法的使用。
IEnumerable
IEnumerable 是一个接口,客户代码通过实现该接口的GetEnumerator方法,来返回一个具体的IEnumerator.。
在实际应用中,IEnumerable 面向内存对象,它会将所有的数据载入到内存中,再进行各种操作。IEnumerable 包含一系列操作内存对象的扩展方法。
IQueryable
IQueryable 是一个接口,一般通过第三方的Data Provider来实现接口中的方法。例如EntityFramework。
在实际应用中,IQueryable 面向Data Provider,为其创建查询的SQL语句。
本文通过一个使用EntityFramework来查询农业银行在天津的分行数据,来说明两个接口在实际使用中的差别。
关键代码如下:
using (var context = new BranchContext(connectionString)){
IEnumerable<Branch> branches = context.Branches.Where( b => b.Id > 1);
branches = branches.Take(2);
foreach(Branch branch in branches){
System.Console.WriteLine(branch.Name);
}
}
关键代码如下:
using (var context = new BranchContext(connectionString)){
IQueryable<Branch> branches = context.Branches.Where( b => b.Id > 1);
branches = branches.Take(2);
foreach(Branch branch in branches){
System.Console.WriteLine(branch.Name);
}
}
IEnumerable中的Linq查询方法,只是针对内存对象。IQueryable中的Linq查询方法,是帮助第三放Data Provider生成查询的SQL语句。
所以我们在做大数据表服务器端分页操作时候,一定要小心。我们必须确保所有的查询方法都是基于IQueryable的,最后都能反应到SQL语句上。避免由于不慎使用 IEnumerable,将大数据表全部载入内存,再进行各种过滤操作的情况。那样做,会使服务器压力激增。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace IQueryableIEnumerable
{
[Table("t_branch")]
public class Branch{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required, Column("Addr")]
public string Address { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
}
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IQueryableIEnumerable
{
public class BranchContext : DbContext {
private readonly ILoggerFactory loggerFactory
= LoggerFactory.Create(ConventionForeignKeyExtensions => ConventionForeignKeyExtensions.AddConsole());
private readonly string ConnectionString;
public DbSet<Branch> Branches {get;set;}
public BranchContext(string connectionString)
{
ConnectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(loggerFactory);
optionsBuilder.UseSqlServer(ConnectionString);
}
}
}
using System;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Collections.Generic;
namespace IQueryableIEnumerable
{
class Program
{
static void Main(string[] args)
{
var connectionString = @"Server=PC-20210618NEZE\SQL2012EXPRESS;Database=BranchMngt;Trusted_Connection=True;MultipleActiveResultSets=true";
using (var context = new BranchContext(connectionString)){
IQueryable<Branch> branches = context.Branches.Where( b => b.Id > 1);
branches = branches.Take(2);
foreach(Branch branch in branches){
System.Console.WriteLine(branch.Name);
}
}
/* using (var context = new BranchContext(connectionString)){
IEnumerable branches = context.Branches.Where( b => b.Id > 1);
branches = branches.Take(2);
foreach(Branch branch in branches){
System.Console.WriteLine(branch.Name);
}
} */
}
}
}
if object_id(N't_branch',N'U') is not null drop table t_branch
create table t_branch (
Id int primary key identity(1,1),
Name nvarchar(50) not null,
Addr nvarchar(200) not null,
[RowVersion] [timestamp] NULL
)
insert into t_branch (Name,Addr) values
(N'天津丽江道支行',N'天津市西青区李七庄街凌口村悦雅花园9号楼底商一层'),
(N'中国农业银行',N'天津市南开区凌宾路'),
(N'天津宾水西道支行',N'天津市南开区宾水西道阳光公寓A座首层、二层'),
(N'天津李七庄支行',N'天津市河西区纪庄子道4号'),
(N'天津阳光支行',N'天津市南开区港宁西路27号'),
(N'环湖中路支行',N'天津市河西区环湖中路10号(13路终点站)')