Lostinet

Lostinet
随笔 - 17, 文章 - 0, 评论 - 283, 引用 - 4
数据加载中……

简单地谈一下如何动态构造Linq查询.

 博客园已经有些帖子告诉大家,如何构造Expression.
但是那些构造Expression的方法是很麻烦的,而且没有理解好IQueryable<T>的含义.
下面是一个很简单的动态构造查询的例子:
(也可以使用传统语法+Lambda:)

string customername="Great";
string shippername="Express";
using (NorthwindContext nc = new NorthwindContext())
{
    var query = from order in nc.Orders select order;
    if (!string.IsNullOrEmpty(customername))
    {
        query = from order in query where order.Customer.CompanyName.Contains(customername) select order;
    }
    if (!string.IsNullOrEmpty(shippername))
    {
        query = from order in query where order.Shipper.CompanyName.Contains(shippername) select order;
    }
    Console.WriteLine(query.ToArray().Count());
}

 exec sp_executesql N'SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
LEFT OUTER JOIN [dbo].[Shippers] AS [t2] ON [t2].[ShipperID] = [t0].[ShipVia]
WHERE ([t2].[CompanyName] LIKE @p0) AND ([t1].[CompanyName] LIKE @p1)', N'@p0 nvarchar(9),@p1 nvarchar(7)', @p0 = N'%Express%', @p1 = N'%Great%'

可以看到的是, query = from order in query .
是的. 一个query对象, 是可以继续被查下去的.
实际上,能做 from order in nc.Orders , 正是因为 nc.Orders 是实现 IQueryable<Order> .

 当我们使用IQueryable<T> query=from x in IQueryable<T> 的时候, 正是从一个查询,添加了新的条件或选择, 返回另外一个查询.
这些查询,一直只是构造中. 如果它的ToArray()或者其他相关方法,(例如IEnumerable.GetEnumerator), 没有被执行, 那么这个查询就不会执行.


也可以使用传统语法+Lambda:
 
if (!string.IsNullOrEmpty(customername))
{
    //query = from order in query where order.Customer.CompanyName.Contains(customername) select order;
    query = query.Where(order => order.Customer.CompanyName.Contains(customername));
}
if (!string.IsNullOrEmpty(shippername))
{
    //query = from order in query where order.Shipper.CompanyName.Contains(shippername) select order;
    query = query.Where(order => order.Shipper.CompanyName.Contains(shippername));
}

 

相关信息:

 

昨天晚上下载了C# Express 2008 VWD Express 2008 , 发现没有ASP.NET3.5…??

, 应该是说, 运行的还是ASP.NET2的环境, 新的语法无法在WEB中动态编译.

Linq的定义,from in where select的语法只能写在某个类库里,然后让Web去调用.

 

顺便用Reflector打开DLinq的实现. 发现原来还是挺简单的.

 

所以等我把现在的工作做完后, 我会推出 Linq To CSPAbstractRecord .

(不知道CSPAbstractRecord是什么,可以看看我这里的主页..)

 

 

posted on 2007-08-22 13:51 Lostinet 阅读(1983) 评论(14)  编辑 收藏 所属分类: WOW

评论

#1楼    回复  引用  查看    

http://www.cnblogs.com/RChen/archive/2007/08/19/861354.html
hehe, 看来我用串接构造查询的方式构造是对的。
前几天试着用这个方式做正式的项目了,动态组合 where 和 orderby 都这么做没有问题。
不过,这个方式就需要组合过程中每一次构造完返回的 IQueriable<T> 的 T 都是一样的,
就是说如果需要 select new {A=a, b=b} 这样的就只能放最后声明一个不同类型的 query 再串接一次了。

2007-08-22 14:16 | 木野狐(Neil Chen)      

#2楼    回复  引用  查看    

想起 ruby 里很多方法调用后都返回自身,看来 “方法调用可串接” 是 FP 的要素之一,linq 的扩展方法实现也有同样的思想啊。
2007-08-22 14:19 | 木野狐(Neil Chen)      

#3楼    回复  引用  查看    

动态拼接排序字段我是这么做的:



因为 OrderBy 和 OrderByDescending 的参数 keySelector 需要一个具体类型,不得不针对 int 型字段和 string 型字段的排序,分别定义一个变量保存不同类型的 lambda 表达式,请教这个有什么更好的办法吗?
2007-08-22 14:32 | 木野狐(Neil Chen)      

#4楼 [楼主]   回复  引用  查看    

@木野狐(Neil Chen) :

string customername="Great";
string shippername="Express";
bool orderbydate = false;
using (NorthwindContext nc = new NorthwindContext())
{
var query = from order in nc.Orders select order;
if (!string.IsNullOrEmpty(customername))
{
query = from order in query where order.Customer.CompanyName.Contains("Great") select order;
}
if (!string.IsNullOrEmpty(shippername))
{
query = from order in query where order.Shipper.CompanyName.Contains(shippername) select order;
}
if (orderbydate)
{
query = query.OrderByDescending(order => order.OrderDate);
}
else
{
query = query.OrderByDescending(order => order.ShipName);
}
Console.WriteLine(query.ToArray().Count());
}



或者:
Expression< Func < Order, string > > shipnameexp = order => order.ShipName;
query = query.OrderByDescending < Order, string > (shipnameexp);

--奇怪.博客园没有处理 < > ?
bbb iii

2007-08-22 14:56 | Lostinet      

#5楼    回复  引用  查看    

为什么要用 ToArray().Count()?如果 ToArray() 也就罢了,但是 Count() 应用于数组上是极端浪费效率的,并且 Count() 不会反映到延迟生成的 T-SQL 中,因为已经先行转换为 Array 了。
2007-08-22 16:02 | 随风流月      

#6楼    回复  引用  查看    

@随风流月
那个只是用于执行查询随便写的一个语句吧。

@Lostinet
谢谢,也只能这样了,不同类型的 Lambda 没法用一个变量保存,在这一点上强类型又显示了它的限制性,不像 python 那么爽。
2007-08-22 16:24 | 木野狐(Neil Chen)      

#7楼    回复  引用  查看    

@Lostinet
--奇怪.博客园没有处理 < > ?
bbb iii

==
本来不就有“使用高级评论”么?呵呵
2007-08-22 16:32 | 木野狐(Neil Chen)      

#8楼 [楼主]   回复  引用  查看    

@ 随风流月
我这个只是例子, 目的就是要它生成选择Order各列的语句. 用于分析.

@木野狐(Neil Chen)
普通的TEXT很应该Encode一下吧.
不然我的帐号被偷了怎么办? 好在我用的是IE, 用FireFox的要注意点了.
2007-08-22 16:49 | Lostinet      

#9楼 [楼主]   回复  引用  查看    

@木野狐(Neil Chen)
下面的编译通过.
Expression < Func < Order, object > > shipnameexp;
bool orderbyname = false;
if (orderbyname)
{
shipnameexp = order => order.ShipName;
}
else
{
shipnameexp = order => order.OrderID;
}
当用OrderID的时候,检查表达式时候抛出错误.

2007-08-22 16:55 | Lostinet      

#10楼    回复  引用  查看    

我晕。楼主写了好多重复的代码。可以这么来做

private void Methods(string orderId)
{
var q = db.Customers.Select(c=>c);

switch(orderId)
{
case "ID":
q = q.OrderBy(c=>c.ID);
break;
case "Name":
q = q.OrderBy(c=>c.Name);
break;
default:
break;
}

var result = q.ToList();
}

当然, 这只是偷懒的办法。要想做的更好些,还是要动态构造Lambda表达式。
2007-08-22 17:15 | 宋国安      

#11楼 [楼主]   回复  引用  查看    

@宋国安
是的.
无论是from表达式,还是直接使用Queryable+Lambda的方法,
最后构造的还是Expression < Func < > >.
不过用语言来帮我们构造,不是很好吗?

也可以引申开来,
凡是接受Expression < Func < > > ,或Func < > 作为参数的方法, 都可以应用Lambda表达式.


2007-08-22 17:32 | Lostinet      

#12楼    回复  引用  查看    

d
2007-08-23 20:59 | Join miao      

#13楼    回复  引用  查看    

到现在对DLinq还是没有什么好感..希望后面会有更多的突破.
针对数据库操作层面上Expression应该可以灵活处理才对
Expression exp = Order.OrderID==1;
exp.Delete<OrderDetails>();
exp.Delete<Orders>();
exp.CountOf<Orders>();
exp.CountOf<OrderDetails>();
exp.List<Orders>();
exp.List<OrderDetails>();
2007-08-24 14:22 | henry      

#14楼    回复  引用    

LINQ: Building an IQueryable Provider:
http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx
就是对编译器提供的Expression树做语义分析.. LINQ TO SQL的性能不理想很大程度上就是因为这些分析过于复杂, 并涉及到reflection的调用.
到实际的应用中如果不用CompiledQuery, 真不知道会慢成什么样...
2007-08-27 14:25 | Adrian H. [未注册用户]

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-08-22 17:39 编辑过


相关链接: