最近在寫代碼的過程中用到了Linq查詢,在查找資料的過程中發現網上的資料千奇百怪,於是自己整理了一些關於Linq中容易讓人困惑的地方。
本文全部代碼基於:UserInfo與Class兩個表,其中Class中的UserId與UserInfo中的Id對應
本文唯一訪問地址:http://www.cnblogs.com/yubaolee/p/BestLinqQuery.html
linq聯合查詢
內聯查詢
內聯是一個實際使用頻率很高的查詢,它查詢兩個表共有的且都不為空的部分
from user in UserInfo
join c in Classes on user.Id equals c.UserId
select new
{
user.UserName,
user.Id,
c.ClassName
}
查詢結果
對應的SQL語句
SELECT [t0].[UserName], [t0].[Id], [t1].[ClassName] FROM [UserInfo] AS [t0] INNER JOIN [Class] AS [t1] ON [t0].[Id] = [t1].[UserId]
左聯查詢
左聯應該是聯合查詢中使用頻率最高的查詢。它以左表為准,進行聯合查詢。如果右表中不存在對應的結果,則置空。(注意:在Linq中是不存在右聯連的說法,因為右聯無非是把左邊的表移動到右邊,查詢的結果與左聯是一樣的)
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
}
查詢結果
對應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集合。所以我們可以得到到左聯的另一種結果:
from user in UserInfo
join c in Classes on user.Id equals c.UserId into temp
select new
{
user,
temp
}
查詢結果(為了更明確表達集合,在Class表里特別加了一條記錄,所以class那邊共有3條)
對應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
}
查詢結果
對應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
}
)
查詢結果
對應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
查詢結果
注意一下查詢結果,外層是一個我們常用的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訪問一下,發現是可以訪問的:
但如果我們這樣使用時:
from user in _context.UserInfoes
select 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會被轉成對應的數據庫字段。因為數據庫中並沒有該字段,所以會出現這個問題。解決的方法很簡單:
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的人肯定知道結果了。
在Linq操作中實體中的屬性必須在配置映射時指定。我們的數據庫中當然沒有UserExt2這個字段,所以增加Ignore標識,或調用一下:
this.Ignore(t => t.UserExt2);










