走进Linq-Linq to Objects(下)实例篇
2008-07-24 09:04 横刀天笑 阅读(3273) 评论(20) 编辑 收藏 举报
以博客园为例建模:
博客园里每个用户有且仅有一个博客,为了简单每篇博客只能属于一个分类,每个用户有一个角色
下面是代码
现在假设博客园程序启动的时候将数据库所有数据读入到内存中(多么荒谬啊,呵呵,仅仅是个假设),填充到上面这些对象里,那我们在内存中就有了这些集合: IList<User> users,IList<Post> posts,IList<Role> roles,我们现在使用Linq to Objects对这些集合进行各种操作
向查询表达式传入参数
现在有一个用户输入http://yuyijq.cnblogs.com,通过UrlRewrite,这个连接将转向到http://www.cnblogs.com/blog.aspx?u=yuyijq,那现在我们的程序要干些什么呢?首先根据yuyijq读取该博主的所有帖子。那就要从IList<Post>集合里查找出UserName为”yuyijq”的所有帖子,但是显示在博客首页上的时候并不需要帖子的内容,如果把帖子内容也读取的话太耗资源了,只需要博客的标题,然后我们将这个只有标题和摘要的对象的集合绑定到一个GridView上:
where post.UserName == "yuyijq"
select post.Title;
mainGridView.DataSource = dataSource;
mainGridView.DataBind();
但是很明显,这个查询是硬编码的,我直接将”yuyijq”传递到查询中,拜延迟计算所赐,我们可以在查询表达式里使用变量了:
var dataSource = from post in posts
where post.UserName == userName
select post.Title;
mainGridView.DataSource = dataSource;
mainGridView.DataBind();
也许你看到上面的代码不会觉得有什么特别,如果我将代码稍微坐下修改:
var dataSource = from post in posts
where post.UserName == userName
select post.Title;
userName = "yuyijq";
mainGridView.DataSource = dataSource;
mainGridView.DataBind();
你觉得最终GridView上绑定的应该是yuyijq的帖子还是zhzkl的帖子?实际执行点经过下面的代码:
where post.UserName == userName
select post.Title;
非泛型集合的查询
虽然今天C#已经发展到了3.0,但是在2.0里出现的泛型并没有得到全面的普及,很多开发者还是在程序里大量使用1.x里出现的一些非泛型集合,比如ArrayList就是个代表,那么如果存在这样一个集合我们怎么去查询:
where post.UserName == userName
select post.Title;
where post.UserName == userName
select post.Title;
排序
在数据驱动的应用中我们经常需要对数据根据一些属性进行排序,而通常这些排序的属性应该是用户可以自己设置的,比如博客,可以根据点击率排序,也可以根据评论排序,甚至两者都作为排序根据。还有什么顺序啊,倒序啊,也就是这个排序是个动态的。那我们是不是要写一大串if…else…进行判断,然后写不同的Linq表达式:
return from post in posts
where post.Title ==”yuyijq”
orderby post.Click
select post;
}else if(…){
…
}
this IEnumerable<TElement> source, Func<TElement, TKey> keySelector)
return from post in posts
where post.UserName = “yuyijq”
orderby selector(post)
select post;
{
return from post in posts
where post.UserName == userName
orderby selector(post)
select post;
}
{
return from post in posts
where post.UserName == userName
isAsc ?Orderby selector(post):orderby selector(post) des
select post;
}
{
var newPosts = from post in posts
where post.UserName == userName
select post;
return isAsc ?newPosts.Orderby(selector(post)):newPosts. OrderByDescending(selector(post));
}
where post.UserName == "yuyijq"
orderby post.Click,post.Comments
select post.Title;
分组
在上一篇中GroupBy对应的查询表达式也非常麻烦,下面就来瞧瞧。我们要对博客园上所有文章做个分组,分组依据就根据博客的用户名好了:
group post.Title by post.UserName;
group post.Title by post.UserName,post.CategoryId;
group post.Title by new { post.UserName, post.CategoryId };
foreach(var item in dataSource)
{
//在遍历这个dataSource的时候,item有一个属性Key,这个Key就代表by后面的东西
}
如果像上面那样,这个查询表达式就要到这里终止了,还有这样一种方式:
group post by post.UserName into grouping
select new {
Key = grouping.Key,
Value = grouping
};
group post by post.UserName into grouping
select new {
Key = grouping.Key,
TotalClick = grouping.Sum(post=>post.Click)
};
联结
Join也是个蛮复杂的表达式
如果给你一个UserId,你要根据UserId从users集合里先找出UserName,然后根据UserName找出所有下面的Post:
join user in users on post.UserName equals user.UserName
where user.UserId = 2
select post;
注意,联结的条件不能用”==”而应该用equals。
总结
本篇文章用实例对一些重点进行了举例说明,当然还有一些更多的用法没有提及,不过对于这些东西大家还是多做练习才能灵活运用。还有Linq的性能的问题,现在很多人担心Linq的性能,确实,Linq的性能还赶不上传统写法的性能。不过在某一些方面它们还是相差不大。但用Linq写一些代码的时候,真的可以获得意想不到的速率,比如有的时候需要对几个集合的数据进行联结或者分组,用传统的做法可能要嵌套几个循环,还可能要创建临时的集合,非常繁琐,如果用Linq则不一样了,Linq不仅仅是一项技术,还是一种编程的风格,至于性能的问题我觉得Rails之父说的那句话很经典:当性能未成为问题之前它永远不是个问题。欢迎大家提出问题,我会对文章进行更新。
PS:2.o里,string类实现了IEnumerable<char>接口,现在你都可以用Linq处理字符串了,有意思吧。
祝编程愉快