linq中的連接操作符
linq中的連接操作符主要包括Join()和GroupJoin()兩個(gè)。
一、Join()操作符
Join()操作符非常類似于T-SQL中的inner join,它將兩個(gè)數(shù)據(jù)源進(jìn)行連接,根據(jù)兩個(gè)數(shù)據(jù)源中相等的值進(jìn)行匹配。例如:可以將產(chǎn)品表和產(chǎn)品類別表進(jìn)行連接,得到產(chǎn)品名稱和與其對(duì)應(yīng)的類型名稱。下面看看Join()方法的定義:
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector); public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer);
從Join()方法的定義中可以看到:Join操作符的方法原型非常復(fù)雜,從方法原型可以看到,參數(shù)outer和inner是需要連接的兩個(gè)輸入集合。其中outer代表的是調(diào)用的集合。當(dāng)Join操作符被調(diào)用時(shí),首先列舉inner序列中的所有元素,為序列中每一個(gè)類型為U的元素調(diào)用委托InnerKeySelector,生成一個(gè)類型為K的的對(duì)象innerKey作為連接關(guān)鍵字。(相當(dāng)于數(shù)據(jù)庫(kù)中的外鍵),將inner序列中的每一個(gè)元素和其對(duì)應(yīng)的連接關(guān)鍵字innerKey存儲(chǔ)在一個(gè)臨時(shí)哈希表中;其次列舉outer序列中的所有元素,為每一個(gè)類型為T的元素調(diào)用委托outerKeySelector,生成一個(gè)類型為K的對(duì)象outKey用作連接關(guān)鍵字,在第一步生成的臨時(shí)哈希表中查找與outKey相等的對(duì)應(yīng)的innerKey對(duì)象,如果找到對(duì)應(yīng)的記錄,會(huì)將當(dāng)前outer序列中的類型為T的元素和對(duì)應(yīng)的inner序列中類型為U的元素作為一組參數(shù)傳遞給委托resultSelector,resultSelector會(huì)根據(jù)這兩個(gè)參數(shù)返回一個(gè)類型為V的對(duì)象,此類型為V的對(duì)象會(huì)被添加到Join操作符的輸出結(jié)果序列中去。Join操作符返回一個(gè)類型為IEnumerable<T>的序列。來(lái)看下面的例子:
1、新建Category和Product兩個(gè)類,其類定義分別如下:
Category:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
public class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
public DateTime CreateTime { get; set; }
}
}Product:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
public class Product
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public DateTime CreateTime { get; set; }
}
}2、在Main()方法中調(diào)用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
class Program
{
static void Main(string[] args)
{
// 初始化數(shù)據(jù)
List<Category> listCategory = new List<Category>()
{
new Category(){ Id=1,CategoryName="計(jì)算機(jī)",CreateTime=DateTime.Now.AddYears(-1)},
new Category(){ Id=2,CategoryName="文學(xué)",CreateTime=DateTime.Now.AddYears(-2)},
new Category(){ Id=3,CategoryName="高校教材",CreateTime=DateTime.Now.AddMonths(-34)},
new Category(){ Id=4,CategoryName="心理學(xué)",CreateTime=DateTime.Now.AddMonths(-34)}
};
List<Product> listProduct = new List<Product>()
{
new Product(){Id=1,CategoryId=1, Name="C#高級(jí)編程第10版", Price=100.67,CreateTime=DateTime.Now},
new Product(){Id=2,CategoryId=1, Name="Redis開(kāi)發(fā)和運(yùn)維", Price=69.9,CreateTime=DateTime.Now.AddDays(-19)},
new Product(){Id=3,CategoryId=2, Name="活著", Price=57,CreateTime=DateTime.Now.AddMonths(-3)},
new Product(){Id=4,CategoryId=3, Name="高等數(shù)學(xué)", Price=97,CreateTime=DateTime.Now.AddMonths(-1)},
new Product(){Id=5,CategoryId=6, Name="國(guó)家寶藏", Price=52.8,CreateTime=DateTime.Now.AddMonths(-1)}
};
// 1、查詢表達(dá)式
var queryExpress = from c in listCategory
join p in listProduct on c.Id equals p.CategoryId
select new { Id = c.Id, CategoryName = c.CategoryName, ProductName = p.Name, PublishTime = p.CreateTime };
Console.WriteLine("查詢表達(dá)式輸出:");
foreach (var item in queryExpress)
{
Console.WriteLine($"id:{item.Id},CategoryName:{item.CategoryName},ProductName:{item.ProductName},PublishTime:{item.PublishTime}");
}
Console.WriteLine("方法語(yǔ)法輸出:");
// 方法語(yǔ)法
var queryFun = listCategory.Join(listProduct, c => c.Id, p => p.CategoryId, (c, p) => new { Id = c.Id, CategoryName = c.CategoryName, ProductName = p.Name, PublishTime = p.CreateTime });
foreach (var item in queryFun)
{
Console.WriteLine($"id:{item.Id},CategoryName:{item.CategoryName},ProductName:{item.ProductName},PublishTime:{item.PublishTime}");
}
Console.ReadKey();
}
}
}結(jié)果:

從結(jié)果中可以看出:Join()操作符只會(huì)輸出兩個(gè)結(jié)合中相同的元素,和T-SQL中的inner join類似。
在T-SQL中除了內(nèi)連接以外,還有左連接和右連接,那么使用Join()是不是也可以實(shí)現(xiàn)左連接和右連接呢?請(qǐng)看下面的例子。
使用Join()實(shí)現(xiàn)左連接。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
class Program
{
static void Main(string[] args)
{
// 初始化數(shù)據(jù)
List<Category> listCategory = new List<Category>()
{
new Category(){ Id=1,CategoryName="計(jì)算機(jī)",CreateTime=DateTime.Now.AddYears(-1)},
new Category(){ Id=2,CategoryName="文學(xué)",CreateTime=DateTime.Now.AddYears(-2)},
new Category(){ Id=3,CategoryName="高校教材",CreateTime=DateTime.Now.AddMonths(-34)},
new Category(){ Id=4,CategoryName="心理學(xué)",CreateTime=DateTime.Now.AddMonths(-34)}
};
List<Product> listProduct = new List<Product>()
{
new Product(){Id=1,CategoryId=1, Name="C#高級(jí)編程第10版", Price=100.67,CreateTime=DateTime.Now},
new Product(){Id=2,CategoryId=1, Name="Redis開(kāi)發(fā)和運(yùn)維", Price=69.9,CreateTime=DateTime.Now.AddDays(-19)},
new Product(){Id=3,CategoryId=2, Name="活著", Price=57,CreateTime=DateTime.Now.AddMonths(-3)},
new Product(){Id=4,CategoryId=3, Name="高等數(shù)學(xué)", Price=97,CreateTime=DateTime.Now.AddMonths(-1)},
new Product(){Id=5,CategoryId=6, Name="國(guó)家寶藏", Price=52.8,CreateTime=DateTime.Now.AddMonths(-1)}
};
// 1、使用查詢表達(dá)式實(shí)現(xiàn)左連接
var listLeft = from c in listCategory
join p in listProduct on c.Id equals p.CategoryId
into cpList
from cp in cpList.DefaultIfEmpty()
select new
{
Id = c.Id,
CategoryName = c.CategoryName,
ProductName = cp == null ? "無(wú)產(chǎn)品名稱" : cp.Name,
PublishTime = cp == null ? "無(wú)創(chuàng)建時(shí)間" : cp.CreateTime.ToString()
};
foreach (var item in listLeft)
{
Console.WriteLine($"id:{item.Id},CategoryName:{item.CategoryName},ProductName:{item.ProductName},PublishTime:{item.PublishTime}");
}
Console.ReadKey();
}
}
}結(jié)果:

從結(jié)果中可以看出:左表listCategory中的數(shù)據(jù)全部輸出了,listCategory中分類為4的在listProduct中沒(méi)有對(duì)應(yīng)的產(chǎn)品記錄,所以該項(xiàng)的ProductName和PublishTime輸出為空。
注意:
在左連接中,有可能右表中沒(méi)有對(duì)應(yīng)的記錄,所以使用Select投影操作符的時(shí)候要注意判斷是否為null,否則程序會(huì)報(bào)錯(cuò),看下面的例子:

使用方法語(yǔ)法實(shí)現(xiàn)左連接要使用下面要講的GroupJoin(),所以使用方法語(yǔ)法實(shí)現(xiàn)左連接放到GroupJoin()中進(jìn)行講解。
右連接
其實(shí)實(shí)現(xiàn)右連接只需要將上面例子中的左表和右邊換一下順序即可。看下面的例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
class Program
{
static void Main(string[] args)
{
// 初始化數(shù)據(jù)
List<Category> listCategory = new List<Category>()
{
new Category(){ Id=1,CategoryName="計(jì)算機(jī)",CreateTime=DateTime.Now.AddYears(-1)},
new Category(){ Id=2,CategoryName="文學(xué)",CreateTime=DateTime.Now.AddYears(-2)},
new Category(){ Id=3,CategoryName="高校教材",CreateTime=DateTime.Now.AddMonths(-34)},
new Category(){ Id=4,CategoryName="心理學(xué)",CreateTime=DateTime.Now.AddMonths(-34)}
};
List<Product> listProduct = new List<Product>()
{
new Product(){Id=1,CategoryId=1, Name="C#高級(jí)編程第10版", Price=100.67,CreateTime=DateTime.Now},
new Product(){Id=2,CategoryId=1, Name="Redis開(kāi)發(fā)和運(yùn)維", Price=69.9,CreateTime=DateTime.Now.AddDays(-19)},
new Product(){Id=3,CategoryId=2, Name="活著", Price=57,CreateTime=DateTime.Now.AddMonths(-3)},
new Product(){Id=4,CategoryId=3, Name="高等數(shù)學(xué)", Price=97,CreateTime=DateTime.Now.AddMonths(-1)},
new Product(){Id=5,CategoryId=6, Name="國(guó)家寶藏", Price=52.8,CreateTime=DateTime.Now.AddMonths(-1)}
};
// 使用查詢表達(dá)式實(shí)現(xiàn)右連接
var listRight = from p in listProduct
join c in listCategory on p.CategoryId equals c.Id
into pcList
from pc in pcList.DefaultIfEmpty()
select new
{
Id = p.Id,
CategoryName = pc == null ? "無(wú)分類名稱" : pc.CategoryName,
ProductName = p.Name,
PublishTime = p.CreateTime
};
foreach (var item in listRight)
{
Console.WriteLine($"id:{item.Id},CategoryName:{item.CategoryName},ProductName:{item.ProductName},PublishTime:{item.PublishTime}");
}
Console.ReadKey();
}
}
}結(jié)果:

從結(jié)果中可以看出:listProduct中的數(shù)據(jù)全部輸出了,listCategory中如果沒(méi)有相應(yīng)的記錄就輸出空值。
注意:
在右連接中也需要和左連接一樣進(jìn)行null值的判斷。
二、GroupJoin()操作符
GroupJoin()操作符常用于返回“主鍵對(duì)象-外鍵對(duì)象集合”形式的查詢,例如“產(chǎn)品類別-此類別下的所有產(chǎn)品”。
GroupJoin操作符也用于連接兩個(gè)輸入序列,但與Join操作符不同稍有不同,Join操作符在列舉outer序列元素時(shí),會(huì)將一個(gè)outer序列元素和其對(duì)應(yīng)的inner序列元素作為一組參數(shù)傳遞給委托resultSelector委托,這就意味著如果某一個(gè)outer序列元素有多個(gè)對(duì)應(yīng)的inner序列元素,Join操作符將會(huì)分多次將outer序列元素和每一個(gè)對(duì)應(yīng)的inner序列元素傳遞給委托resultSelector。使用GroupJoin操作符時(shí),如果某一個(gè)outer序列元素有多個(gè)對(duì)應(yīng)的inner序列元素,那么這多個(gè)對(duì)應(yīng)的inner序列元素會(huì)作用一個(gè)序列一次性傳遞給委托resultSelecotr,可以針對(duì)此序列添加一些處理邏輯。下面看看GroupJoin()操作符的定義:
public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector); public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, IEqualityComparer<TKey> comparer);
留意變紅的那個(gè)委托,注意,與Join操作符的不同點(diǎn)就是一個(gè)outer序列中如果有多個(gè)對(duì)應(yīng)的inner序列元素,會(huì)作為一個(gè)集合IEnumerable<TInner>傳遞到此委托。來(lái)看下面的例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
class Program
{
static void Main(string[] args)
{
// 初始化數(shù)據(jù)
List<Category> listCategory = new List<Category>()
{
new Category(){ Id=1,CategoryName="計(jì)算機(jī)",CreateTime=DateTime.Now.AddYears(-1)},
new Category(){ Id=2,CategoryName="文學(xué)",CreateTime=DateTime.Now.AddYears(-2)},
new Category(){ Id=3,CategoryName="高校教材",CreateTime=DateTime.Now.AddMonths(-34)},
new Category(){ Id=4,CategoryName="心理學(xué)",CreateTime=DateTime.Now.AddMonths(-34)}
};
List<Product> listProduct = new List<Product>()
{
new Product(){Id=1,CategoryId=1, Name="C#高級(jí)編程第10版", Price=100.67,CreateTime=DateTime.Now},
new Product(){Id=2,CategoryId=1, Name="Redis開(kāi)發(fā)和運(yùn)維", Price=69.9,CreateTime=DateTime.Now.AddDays(-19)},
new Product(){Id=3,CategoryId=2, Name="活著", Price=57,CreateTime=DateTime.Now.AddMonths(-3)},
new Product(){Id=4,CategoryId=3, Name="高等數(shù)學(xué)", Price=97,CreateTime=DateTime.Now.AddMonths(-1)},
new Product(){Id=5,CategoryId=6, Name="國(guó)家寶藏", Price=52.8,CreateTime=DateTime.Now.AddMonths(-1)}
};
// 使用GroupJoin()實(shí)現(xiàn)左連接
var listLeftFun = listCategory.GroupJoin(listProduct, c => c.Id, p => p.CategoryId, (c, listp) => listp.DefaultIfEmpty(new Product()).Select(z =>
new
{
Id = c.Id,
CategoryName = c.CategoryName,
ProduceName = z.Name,
ProductPrice = z.Price
})).ToList();
foreach (var item in listLeftFun)
{
foreach (var p in item)
{
Console.WriteLine($"CategoryId:{p.Id},CategoryName:{p.CategoryName},ProduceName:{p.ProduceName},ProductPrice:{p.ProductPrice}");
}
}
Console.ReadKey();
}
}
}結(jié)果:

結(jié)果可以看出:使用GroupJoin()操作符可以用Lambda表達(dá)式實(shí)現(xiàn)左連接。
右連接
只需要調(diào)整上面例子中兩張表的順序即可。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOperation
{
class Program
{
static void Main(string[] args)
{
// 初始化數(shù)據(jù)
List<Category> listCategory = new List<Category>()
{
new Category(){ Id=1,CategoryName="計(jì)算機(jī)",CreateTime=DateTime.Now.AddYears(-1)},
new Category(){ Id=2,CategoryName="文學(xué)",CreateTime=DateTime.Now.AddYears(-2)},
new Category(){ Id=3,CategoryName="高校教材",CreateTime=DateTime.Now.AddMonths(-34)},
new Category(){ Id=4,CategoryName="心理學(xué)",CreateTime=DateTime.Now.AddMonths(-34)}
};
List<Product> listProduct = new List<Product>()
{
new Product(){Id=1,CategoryId=1, Name="C#高級(jí)編程第10版", Price=100.67,CreateTime=DateTime.Now},
new Product(){Id=2,CategoryId=1, Name="Redis開(kāi)發(fā)和運(yùn)維", Price=69.9,CreateTime=DateTime.Now.AddDays(-19)},
new Product(){Id=3,CategoryId=2, Name="活著", Price=57,CreateTime=DateTime.Now.AddMonths(-3)},
new Product(){Id=4,CategoryId=3, Name="高等數(shù)學(xué)", Price=97,CreateTime=DateTime.Now.AddMonths(-1)},
new Product(){Id=5,CategoryId=6, Name="國(guó)家寶藏", Price=52.8,CreateTime=DateTime.Now.AddMonths(-1)}
};
var listRightFun = listProduct.GroupJoin(listCategory, p => p.CategoryId, c => c.Id, (p, listc) => listc.DefaultIfEmpty(new Category()).Select(z =>
new
{
id = p.Id,
ProductName = p.Name,
ProductPrice = p.Price,
CategoreName = z.CategoryName
}));
foreach (var item in listRightFun)
{
foreach (var p in item)
{
Console.WriteLine($"id:{p.id},ProduceName:{p.ProductName},ProductPrice:{p.ProductPrice},CategoryName:{p.CategoreName}");
}
}
}
}
}結(jié)果:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
.net 獲取瀏覽器Cookie(包括HttpOnly)實(shí)例分享
這篇文章介紹了.net 獲取瀏覽器Cookie(包括HttpOnly)實(shí)例,有需要的朋友可以參考一下2013-10-10
巧用ASP.NET預(yù)編譯Web應(yīng)用程序規(guī)避調(diào)用延遲的方法
ASP.NET 1.x的開(kāi)發(fā)人員常常聽(tīng)到用戶抱怨首次調(diào)用應(yīng)用程序的時(shí)候會(huì)碰到初始化延遲。畢竟,初次請(qǐng)求會(huì)引發(fā)一個(gè)系列過(guò)程,包括運(yùn)行庫(kù)初始化、分析、把ASPX頁(yè)面編譯成中間語(yǔ)言、把方法即時(shí)編譯成本地代碼等等。2011-08-08
asp.net 頁(yè)面?zhèn)髦档膸讉€(gè)方法
在網(wǎng)頁(yè)應(yīng)用程序的開(kāi)發(fā)中,頁(yè)面之間的傳值應(yīng)該是最常見(jiàn)的問(wèn)題了。2009-11-11
ASP.NET對(duì)HTML頁(yè)面元素進(jìn)行權(quán)限控制(二)
分三步實(shí)現(xiàn):標(biāo)出界面所要分配權(quán)限的元素、掃描界面獲取所要分配權(quán)限的元素信息、存入數(shù)據(jù)庫(kù)中,感興趣的朋友可以了解下2013-12-12
VS2019中.NET如何實(shí)現(xiàn)打日志功能
本文主要介紹了VS2019中.NET如何實(shí)現(xiàn)打日志功能,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
asp.net中動(dòng)態(tài)改變網(wǎng)頁(yè)標(biāo)題的代碼
asp.net中動(dòng)態(tài)改變網(wǎng)頁(yè)標(biāo)題的代碼,需要的朋友可以參考下。2011-02-02
使用Ajax更新ASP.Net MVC項(xiàng)目中的報(bào)表對(duì)象方法
下面小編就為大家分享一篇使用Ajax更新ASP.Net MVC項(xiàng)目中的報(bào)表對(duì)象方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
利用VS2019創(chuàng)建Web項(xiàng)目并發(fā)送到IIS及IIS與ASP.NET配置教程
這篇文章主要介紹了利用VS2019創(chuàng)建Web項(xiàng)目,并發(fā)送到IIS,以及IIS與ASP.NET配置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03

