.NET深刻实战系列—Linq to Sql进阶

.NET深刻实战系列—Linq to Sql进阶

 

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

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

image

 本文惟一访问地址:http://www.cnblogs.com/yubaolee/p/BestLinqQuery.html数据库

linq联合查询

内联查询


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

from user in UserInfo  
join c in Classes on user.Id equals c.UserId  
select new  
{  
    user.UserName,  
    user.Id,  
 c.ClassName  
} 

查询结果测试

image

对应的SQL语句this

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

左联查询


from user in UserInfo    
join c in Classes on user.Id equals c.UserId into temp    
from c in temp.DefaultIfEmpty()    
select new    
{    
     user.UserName,    
     user.Id,    
     c.ClassName    
}

查询结果url

image

对应SQL语句3d

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集合。因此咱们能够获得到左联的另外一种结果:code

from user in UserInfo  
join c in Classes on user.Id equals c.UserId into temp  
select new  
{  
    user,  
    temp  
}  

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

image

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

SELECT t0.*, [t1].[Id] AS [Id2], t1.*, (  
    SELECT COUNT(*)  
    FROM [Class] AS [t2]  
    WHERE [t0].[Id] = [t2].[UserId]  
    ) AS [value]  
FROM [UserInfo] AS [t0]  
LEFT OUTER JOIN [Class] AS [t1] ON [t0].[Id] = [t1].[UserId]  

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

from user in UserInfo  
from c in Classes  
select new  
{  
    user.UserName,  
    user.Id,  
 c.ClassName  
}  

查询结果

image

对应SQL语句

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

合并(Union)


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

(  
    from userinfo in UserInfo  
    select new {  
      Id = (System.Int32?)userinfo.Id,  
      Name = userinfo.UserName  
    }  
).Union  
(  
    from c in Classes  
      select new {  
      Id = (System.Int32?)c.UserId,  
      Name = c.ClassName  
    }  
)  

查询结果

image

对应SQL语句

SELECT [t0].[Id] AS [value], [t0].[UserName]  
FROM [UserInfo] AS [t0]  
UNION  
SELECT [t1].[UserId] AS [value], [t1].[ClassName]  
FROM [Class] AS [t1]

Linq 分组查询

分组查询(group by)也是咱们在实际项目中一个经常使用的操做,查询操做以下:

from c in Classes  
group c by c.UserId into temp  
select temp 

查询结果

image

 

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

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

var result = from c in _context.Classes  
               group c by c.UserId  
               into temp  
               select temp;  
  
           foreach (var c in result)  
           {  
               Console.WriteLine(c.Key);  
               foreach (var citem in c)  
               {  
                   Console.WriteLine(citem.ClassName);  
               }  
           } 

实体增长字段处理

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

public partial class UserInfo  
   {  
       public int Id { get; set; }  
       public string UserName { get; set; }  
       public string UserType { get; set; }  
       public int Money { get; set; }  
   }

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

public partial class UserInfo  
    {  
        /// <summary>  
        /// 测试扩展属性  
        /// </summary>  
        public string UserExt  
        {  
            get { return UserName + ":" + UserType; }  
        }  
    } 

而后咱们用EF访问一下,发现是能够访问的:

image

但若是咱们这样使用时:

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

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

image

具体错误信息以下: 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会被转成对应的数据库字段。由于数据库中并无该字段,因此会出现这个问题。解决的方法很简单:

from user in _context.UserInfoes.ToList()  
select new    
{    
    user.Id,    
    user.UserExt    
};   

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

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

public partial class UserInfo  
   {  
       public string UserExt  
       {  
           get { return UserName + ":" + UserType; }  
       }  
       //新增一个完整的属性  
       public string UserExt2 { get; set; }  
   }

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

image

在Linq操做中实体中的属性必须在配置映射时指定。咱们的数据库中固然没有UserExt2这个字段,因此增长Ignore标识,或调用一下:

this.Ignore(t => t.UserExt2); 
相关文章
相关标签/搜索