查询的两种过滤方法:
1.linq to EF 数据库中过滤: 下图我们能看出来Linq表达式在执行的时候已经为我们生成高效的sql语句,
DemoTestEntities dbContext = new DemoTestEntities(); var demoTest = from u in dbContext.UserInfo where u.UserId>0 select u; foreach (var item in demoTest) { Console.WriteLine(item.Age + "," + item.Name); }
打开Sql Server Profiler ,看下到底生成了什么样的sql语句
linq返回值用var来替换,其实它返回值是IQueryable<>类型,
f12能看到它继承(暂这样理解)IEnumerable<T>,IQuery,IEnumerable。
扩展下IQueryable<out T>中这个out起什么作用呢?我们就先简单的了解下"协变"和"逆变":
协变out(安全):就是把一个子类的泛型集合赋值给父类的泛型集合,调用的时候用的是父类的泛型集合,一般用在方法的返回值,所以当集合中有out的时候可以用这个集合的父类来替换它接收数据。
代码如下:
//IQueryable可以用IEnumerable来替换或者替换成IQueryable<object> IQueryable<UserInfo> demoTest = from u in dbContext.UserInfo where u.UserId>0 select u;
//也可以把子类的泛型集合赋值给父类泛型集合
IQueryable<object> objTest=demoTest;
//也可以直接接收
IEnumerable<UserInfo> demoTest = from u in dbContext.UserInfo where u.UserId>0 select u; IQueryable<object> demoTest = from u in dbContext.UserInfo where u.UserId>0 select u;
逆变in(安全):把父类的泛型赋值给子类泛型集合,这有点难理解,为啥父类赋值给子类是安全的呢?
原因是当子类赋值给父类时,父类还是调用自己的方法。逆变难点是"谁在用传过来的这个参数",下面的代码看似是把父类的泛型约束给子类,但是不用,真正调用的时候是在{}括号里面,关键再用的时候要保证安全
Action<object> action = (a) => { Console.WriteLine(a.GetType().Name); }; Action<UserInfo> sunAction = action; sunAction(new UserInfo());
从上面介绍可以知道,所有的返回参数都是协变(也可以理解成外部调用),所有的传入参数都是逆变(也可以理解成内部调用)。在编译阶段会把代码补充完整,本质还是内部进行转换,只不过是编译不对其报错,其实就是语法糖。
2.Linq to object内存过滤:把数据库中所有的数据都查询到程序中,在进行过滤(大数据时,不易用这种)
DemoTestEntities dbContext = new DemoTestEntities(); //把数据从UserInfo表中所有数据都取出来,转换成List集合,然后在遍历这集合过滤
//list集合存放在当前程序的内存中,所以说这种方法是本地过滤
var demoTest = from u in dbContext.UserInfo.ToList() where u.UserId>0 select u; foreach (var item in demoTest) { Console.WriteLine(item.Age+","+item.Name); }
下面简单分析下两种方式的本质区别:
其实这两种方式其实是IQueryable接口集合和List集合的区别,
1.IQueryable
用F12看下IQueryable<> 有个IQueryable类,在类中有三个属性ElementType,Expression,Provider:
下图介绍了三个属性在初始化和调用时起到了什么作用,也可以解释为啥linq能to 各种(to sql,to obj,to ef,to xml):
通过IL看到linq表达式都编译成一个Expression,所有都转换成一个表达式树
2.List