打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
.NET深入实战系列

最近在写代码的过程中用到了Linq查询,在查找资料的过程中发现网上的资料千奇百怪,于是自己整理了一些关于Linq中容易让人困惑的地方。

本文全部代码基于:UserInfo与Class两个表,其中Class中的UserId与UserInfo中的Id对应

 本文唯一访问地址:http://www.cnblogs.com/yubaolee/p/BestLinqQuery.html

linq联合查询

内联查询

内联是一个实际使用频率很高的查询,它查询两个表共有的且都不为空的部分

  1. from user in UserInfo  
  2. join c in Classes on user.Id equals c.UserId  
  3. select new  
  4. {  
  5.     user.UserName,  
  6.     user.Id,  
  7.  c.ClassName  
  8. }  

查询结果

对应的SQL语句

  1. SELECT [t0].[UserName], [t0].[Id], [t1].[ClassName]  
  2. FROM [UserInfo] AS [t0]  
  3. INNER JOIN [Class] AS [t1] ON [t0].[Id] = [t1].[UserId]  

左联查询

左联应该是联合查询中使用频率最高的查询。它以左表为准,进行联合查询。如果右表中不存在对应的结果,则置空。(注意:在Linq中是不存在右联连的说法,因为右联无非是把左边的表移动到右边,查询的结果与左联是一样的)

  1. from user in UserInfo    
  2. join c in Classes on user.Id equals c.UserId into temp    
  3. from c in temp.DefaultIfEmpty()    
  4. select new    
  5. {    
  6.      user.UserName,    
  7.      user.Id,    
  8.      c.ClassName    
  9. }    

查询结果

对应SQL语句

SELECT [t0].[UserName], [t0].[Id], [t1].[ClassName] AS [ClassName]FROM [UserInfo] AS [t0]LEFT OUTER JOIN [Class] AS [t1] ON [t0].[Id] = [t1].[UserId]

!注意一下左联那个【temp】,它其实是一个IEnumerable集合。所以我们可以得到到左联的另一种结果:

  1. from user in UserInfo  
  2. join c in Classes on user.Id equals c.UserId into temp  
  3. select new  
  4. {  
  5.     user,  
  6.     temp  
  7. }  

查询结果(为了更明确表达集合,在Class表里特别加了一条记录,所以class那边共有3条)

对应SQL语句,与左联的SQL基本一样,但多了一个统计行数的列

  1. SELECT t0.*, [t1].[Id] AS [Id2], t1.*, (  
  2.     SELECT COUNT(*)  
  3.     FROM [Class] AS [t2]  
  4.     WHERE [t0].[Id] = [t2].[UserId]  
  5.     ) AS [value]  
  6. FROM [UserInfo] AS [t0]  
  7. LEFT OUTER JOIN [Class] AS [t1] ON [t0].[Id] = [t1].[UserId]  

全联接

全联连是得到两个表的交叉结果(在SQL中称为cross join),这种联连方式得到的结果在没有过滤条件的情况下,基本没什么用。看看即可,代码如下:

  1. from user in UserInfo  
  2. from c in Classes  
  3. select new  
  4. {  
  5.     user.UserName,  
  6.     user.Id,  
  7.  c.ClassName  
  8. }  

查询结果

对应SQL语句

  1. SELECT [t0].[UserName], [t0].[Id], [t1].[ClassName]  
  2. FROM [UserInfo] AS [t0], [Class] AS [t1]  

合并(Union)

这种查询其实也很少用,但在某些变态业务需求下会非常有用,注意查询的结果。它是合并两个表相同列数的结果,并且如果结果中有相同的行,那么只取一行记录。

  1. (  
  2.     from userinfo in UserInfo  
  3.     select new {  
  4.       Id = (System.Int32?)userinfo.Id,  
  5.       Name = userinfo.UserName  
  6.     }  
  7. ).Union  
  8. (  
  9.     from c in Classes  
  10.       select new {  
  11.       Id = (System.Int32?)c.UserId,  
  12.       Name = c.ClassName  
  13.     }  
  14. )  

查询结果

对应SQL语句

  1. SELECT [t0].[Id] AS [value], [t0].[UserName]  
  2. FROM [UserInfo] AS [t0]  
  3. UNION  
  4. SELECT [t1].[UserId] AS [value], [t1].[ClassName]  
  5. FROM [Class] AS [t1]  

Linq 分组查询

分组查询(group by)也是我们在实际项目中一个常用的操作,查询操作如下:

  1. from c in Classes  
  2. group c by c.UserId into temp  
  3. select temp  

查询结果

 

注意一下查询结果,外层是一个我们常用的IQueryable<T>,里面是一个类似Dictionary的K-V集合。简单点说Group返回的是一个集合的集合,因此输出时需用双重循环。

我们在使用它的结果时,应该这样调用:

  1. var result = from c in _context.Classes  
  2.                group c by c.UserId  
  3.                into temp  
  4.                select temp;  
  5.   
  6.            foreach (var c in result)  
  7.            {  
  8.                Console.WriteLine(c.Key);  
  9.                foreach (var citem in c)  
  10.                {  
  11.                    Console.WriteLine(citem.ClassName);  
  12.                }  
  13.            }  

实体增加字段处理

我在本文例子中的UserInfo实体类如下:

  1. public partial class UserInfo  
  2.    {  
  3.        public int Id { get; set; }  
  4.        public string UserName { get; set; }  
  5.        public string UserType { get; set; }  
  6.        public int Money { get; set; }  
  7.    }  

现在我想在该实体中类中添加一个属性。为了保持原实体类的纯洁。我添加一个新的partial类:

  1. public partial class UserInfo  
  2.     {  
  3.         /// <summary>  
  4.         /// 测试扩展属性  
  5.         /// </summary>  
  6.         public string UserExt  
  7.         {  
  8.             get { return UserName + ":" + UserType; }  
  9.         }  
  10.     }  

然后我们用EF访问一下,发现是可以访问的:

但如果我们这样使用时:

from user in _context.UserInfoesselect new{    user.Id,    user.UserExt};

会发现编译是没有问题的。但运行时会出现下面异常:

具体错误信息如下: The specified type member 'UserExt' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

即"UserExt"类型并不能被linq支持。因为在进入到foreach进行真正取数据之前。EF已经把linq转成SQL语句,而UserExt会被转成对应的数据库字段。因为数据库中并没有该字段,所以会出现这个问题。解决的方法很简单:

  1. from user in _context.UserInfoes.ToList()  
  2. select new    
  3. {    
  4.     user.Id,    
  5.     user.UserExt    
  6. };   

即先执行ToList(),提前让linq进行执行,生成UserInfo集合,这样就可以正常访问UserExt了。别看这个小小的改动。在多表联查过滤字段的情况下,你会体会到无尽的妙处!

你可能会想到一个问题,如果我再加一个完整的属性会出现什么情况?

  1. public partial class UserInfo  
  2.    {  
  3.        public string UserExt  
  4.        {  
  5.            get { return UserName + ":" + UserType; }  
  6.        }  
  7.        //新增一个完整的属性  
  8.        public string UserExt2 { get; set; }  
  9.    }  

上面的UserExt2是我们新加入的一个属性,现在我们来执行一下查询。我想真正去研究过Linq的人肯定知道结果了。

在Linq操作中实体中的属性必须在配置映射时指定。我们的数据库中当然没有UserExt2这个字段,所以增加Ignore标识,或调用一下:

  1. this.Ignore(t => t.UserExt2);  
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
MyBatis动态SQL详解
用JAVA从数据库中读出字段及内容
SQLServer子查询相关知识笔记
.net网页开发中的三层架构
asp.net夜话之七:ADO.NET介绍
oracle建立自动增长字段
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服