.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); 
相關文章
相關標籤/搜索