返回 IQueryable<T> OR List<T>

原文摘自:http://tech.it168.com/d/2008-01-16/200801161604779.shtml

这是我决定将LINQ to SQL集成到应用程序中一开始就一直在考虑的一个问题, 到底应该返回IQueryable<T>还是IQueryable? 或许这个列表还应该继续扩展为T,List<T>, object[], 对于Business Layer来说, 到底应该选择哪一种?

  我最后的选择是T和IQueryable<T>, T很明显是针对于单个实体对象, 我认为针对只返回一个确定对象的情况下, 客户端很少需要再次利用IQueryable的能力进行二次筛选和操作, 而且方法体可以明确表明就是这返回一个对象或者null, 这一点我想应该没有太多异议.

  对于集合列表, 的确是一个很难得选择, 我一次试图直接返回List, 这是很直观的, 因为2.0的时候我一直就是这么干的, 所以很自然我认为LINQ to SQL不应该有什么问题会阻止我这么做? 实际上, 这也的确没有什么太多的问题, 例如:

 
 
var q = from c in dataContext.ClaimRequests where c.NextProcessedBy == userID && c.CurrentState == state select c; return q.ToList < ClaimRequest > ();

  ok, 进一步的需求来了, 首先我需要一个分页功能; 到这一步的时候我又有几个选择, 利用LINQ to SQL可以执行自定义存储过程的功能, 完全自己写, 这个可以工作很好, 问题是我很懒, 更重要的时候LINQ to SQL本身已经有API提供了分页功能了, 我有什么理由放弃, 除非性能真的到了非常Critical的时候, 看看下面的分页API, 多么简单:

 
 
return q.Skip < ClaimRequest > ((currentPage - 1 ) * pageSize).Take < ClaimRequest > (pageSize)

  生成的T-SQL语句:

 
 
SELECT [ t2 ] . [ ClaimRequestID ] , [ t2 ] . [ ClaimNumber ] , [ t2 ] . [ ClaimName ] , [ t2 ] . [ DateSubmitted ] , [ t2 ] . [ SubmittedBy ] , ( [ t2 ] . [ LastName ] + @p2 ) + [ t2 ] . [ FirstName ] AS [ SubmittedUserName ] FROM ( SELECT TOP ( 10 ) [ t0 ] . [ ClaimRequestID ] , [ t0 ] . [ ClaimNumber ] , [ t0 ] . [ ClaimName ] , [ t0 ] . [ SubmittedBy ] , [ t0 ] . [ DateSubmitted ] , [ t1 ] . [ FirstName ] , [ t1 ] . [ LastName ] FROM [ dbo ] . [ ClaimRequest ] AS [ t0 ] INNER JOIN [ dbo ] . [ Users ] AS [ t1 ] ON [ t1 ] . [ UserID ] = [ t0 ] . [ SubmittedBy ] WHERE ( [ t0 ] . [ NextProcessedBy ] = @p0 ) AND ( [ t0 ] . [ CurrentState ] = @p1 ) ) AS [ t2 ] @p0 : Input NVarChar (Size = 3 ; Prec = 0 ; Scale = 0 ) [ jlv ] @p1 : Input NVarChar (Size = 3 ; Prec = 0 ; Scale = 0 ) [ New ] @p2 : Input NVarChar (Size = 2 ; Prec = 0 ; Scale = 0 ) [ , ]

  DLINQ生成的SQL语句是利用TOP和嵌套子查询, 这种方法已经被证明是比较高效的做法(相比于临时表的做法), 所以完全有理由可以一试.到这里, List, IQueryable, IQueryable都没有任何问题.

  接下来我还需要一个动态排序功能, 这里List的局限性出来了, 传统的做法可能需要用一个dynamic参数来传递需要排序的列然后到SP当中来执行, 但这里我们已经不打算使用SP了, 也没有动态sql语句, 所有的东西都是强类型的, 然后有LINQ to SQL在运行时来帮我们转换为T-SQL语句。首先List的话, 我们不知道到底哪个字段要排序, 如果使用字符串作为参数的话, 例如放一个string sortBy作为方法的参数, 那么在方法体内就需要做if…else或者switch的判断, 而且还要考虑倒序还是正序的排序要求, 而且你还要hard code,很明显麻烦来了.

  然而如果使用IQueryable却可以很好的解决所有的这些问题. 这里需要说明一下IQueryable<T>和IQueryable, 在没有涉及排序之前, 我想当然的选择了IQueryable, 原因是我不想返回实体类的所有字段以及它的Association类的所有字段,因为这样可以提供更好的性能, 所以自然而然的我选择了返回匿名类, 类似这样:

 
 
var q = from c in dataContext.ClaimRequests where c.NextProcessedBy == userID && c.CurrentState == “New” select new {ClaimRequestID = c.ClaimRequestID, ClaimNumber = c.ClaimNumber, ClaimName = c.ClaimName, DateSubmitted = c.DateSubmitted, SubmittedBy = c.SubmittedBy, SubmittedUserName = c. User .LastName + “, “ + c. User .FirstName};

 

但是我没有考虑到IQueryable不能跨assembly, 一旦跨了assembly的话, 你无法使用var来引用这个匿名类里面的property, 绑定到control是没有问题的, 但是客户端的动态查询却成了问题, 因为你根本不知道匿名类是什么.
 
  最后让我们来看最后的选择IQueryable<T>, 我们选择返回IQueryable<T>给客户端, 分页 / 排序都没有任何问题, 且默认情况下我们返回了实体类的所有字段, 至于是否返回了Association类的信息则取决于绑定情况, 而且这里要非常注意, 如果你不小心在前端绑定代码中使用Association类的字段, 例如ClaimRequest.User.LastName这类的信息很有可能会执行n条的SQL语句, 这个要非常注意, 时刻注意生成的SQL语句的情况是个好的习惯. 为了取得更好的性能, 我还是希望能够使用匿名类来过滤我不需要的字段, IQueryable<T>做到这个完全没有问题, 类似这样:

 
 
var q = ClaimRequestManager.GetPagedClaimRequests(((MasterPage)this.Master).GetUserID(), STATE_NAME, myPager.PageSize, myPager.PageIndex + 1 , out totalRecords); // fields what we need var filter = q. Select (c => new { ClaimRequestID = c.ClaimRequestID, ClaimNumber = c.ClaimNumber, ClaimName = c.ClaimName, DateSubmitted = c.DateSubmitted, SubmittedBy = c.SubmittedBy, SubmittedUserName = c. User .LastName + “, “ + c. User .FirstName } ); rptValidatingReqList.DataSource = filter; rptValidatingReqList.DataBind();

  GetPagedClaimRequests实际上包含了ClaimRequest所有字段的信息, 当然并没有实际执行, 除非你绑定到控件上. 然后客户端可以自己选择到底要使用哪些字段, 尤其IQueryable<T>包含了T所关联的所有信息, 我们可以选择关系类的其他字段进行操作, 过滤后最终进行绑定, 最终我们选择了IQueryable<T>, 强类型 + 灵活的动态查询一样也不差. 有了IQueryable<T>, 某些情况下我们可能就要和匿名类打交道了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值