在DAL层,我们一般都是返回IQueryable类型的数据,然后根据情况在BLL或者UI层来ToList() 【如果是在UI层ToList()其实就是foreach(var item in ...)】
EF数据延迟加载
当你使用Where(),Find(),First().....等等来查询数据的时候,EF仅仅是生成了SQL语句,只有当你真正要使用数据的时候,即在ToList() 或者foreach(var a in list)的时候EF才会将这条SQL语句发送给ado.net,然后ado.net发送给db,进行查询
IQueryable继承自IEnumerable,所以对于数据遍历来说,它们没有区别。
但是IQueryable的优势是它有表达式树,所有对于IQueryable的过滤,排序等操作,都会先缓存到表达式树中,只有当真正遍历发生的时候,才会将表达式树由IQueryProvider执行获取数据操作。
而使用IEnumerable,所有对于IEnumerable的过滤,排序等操作,都是在内存中发生的。也就是说数据已经从数据库中获取到了内存中,只是在内存中进行过滤和排序操作。
在实际检验过程中我们发现 IEnumerable和IQueryable的效率存在差别:
在数据较多的情况下或者操作比较复杂的情况下,IEnumerable的效率会比IQueryable低很多。
Skip():跳过的总能条数
Take():获取的总条数
<pre name="code" class="csharp">using System; using System.Collections.Generic; using System.Data.Entity.Infrastructure; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcTest.Controllers { public class TestController : Controller { salesEntities db = new salesEntities(); //IQueryable接口与IEnumberable接口的区别: IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令,它并不是把所有数据都加载到内存里来才进行条件过滤。 //让我们先来了解一下DbQuery,IQueryable,IQueryable之间的关系,看下面两天代码 //public abstract class DbQuery : IOrderedQueryable, IQueryable, IEnumerable, IListSource //public interface IQueryable : IEnumerable public ActionResult Index() { //它执行的时候SQL语句是 :select * from T_User (这想想为什么他的SQL语句是select * from T_User ,而不是像下面例子那样?其实道理很简单,因为 我们在(from a in db.T_User select a)的后面就toList()了,我们只是如果ToList()就是执行数据库,所以此时它已经去执行数据库了。将这条语句查询到的所有数据都加载到了内存当中,然后再执行后面的.ToList().Skip(5).Take(10); 也就是说,它是在内存中的数据进行刷选了。所以它执行的sql语句就是select * from T_User了 //它会把所有的数据都查询出来,放到内存中,然后再从这些所有的数据中再跳过5条,取10条数据,即取:6-15条的数据 ; IEnumerable<T_User> query1 = (from a in db.T_User select a).ToList().Skip(5).Take(10); //【注意:ToList()方法的返回类型是:IEnumerable<T>】 //--------------------------------------------------------------------------------------------- //我们知道ToList()的返回类型是:IEnumerable<T>所以在接收数据的时候我们需要用IEnumerable<T_User>去接收数据。那我们前面说到IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了 ,为什么这里却是先准备了select top 10* from....语句?之后再执行? //因为我们是最后ToList()的,所以它是先准备select top 10* from (select * ,ROW_NUMBER() over(order by id asc) as rowNum from T_User ) as t1 where t1.rowNum>5 order by t1.Id asc 这么一条sql语句,然后再ToList()的时候去查询数据库 //它只会查询在数据库中第6-15条之间的数据 即取:6-15条的数据 IEnumerable<T_User> query2 = (from a in db.T_User select a).OrderBy(r => r.Id).Skip(5).Take(10).ToList(); //注意:ToList()的返回类型是:IEnumerable<T> //注意:OrderBy()与Skip()Take()方法的返回类型是IQueryable<T> //它只会查询在数据库中第6-15条之间的数据 即取:6-15条的数据 它执行的时候SQL语句是 //select top 10* from (select * ,ROW_NUMBER() over(order by id asc) as rowNum from T_User ) as t1 where t1.rowNum>5 order by t1.Id asc IQueryable<T_User> query3 = (from a in db.T_User select a).OrderBy(r => r.Id).Skip(5).Take(10); //其实这里的query3真实的类型是DbQuery<T_User> 之所以我们在这里用IQueryable<T_User>来接收它,是因为OrderBy()与Skip()Take()方法的返回类型是IQueryable<T>而DbQuery这个类又继承了IQueryable, IEnumerable这两个接口,根据李氏定理,子类可以隐式的转换成父类 所以,这里可以用IQueryable<T_User>或者用IEnumerable<T_User>类接收 //EF之所以可以实现延迟加载,就是因为有了DbQuery这个类。(所谓延迟加载就是:只有当使用到数据的时候才去查询数据库) //DbQuery<T_User> query = ((from a in db.T_User // select a).OrderBy(r => r.Id).Skip(5).Take(10)) as DbQuery<T_User>; ViewBag.Data = query3; return View(); } //就算执行到“}”这一步的时候 query3都是没有数据的。它仅仅是准备了一条sql语句。并没有执行这条语句。也就是说他没有查询数据库(它真正的去查询数据库的时候是在页面上使用的时候 比如在视图Index页面上执行@foreach(var a in viewBag.Data)的时候才会去查询数据库 } }
Index视图
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src="~/Scripts/jquery-1.8.2.js"></script> </head> <body> <div> @foreach (var i in ViewBag.Data) { @i.UserName <br/> } </div> </body> </html>