本文章是根據 微軟MVP solenovex(楊旭)老師的視頻教程撰寫而來,再加上自己的一些理解,
視頻教程地址:https://www.bilibili.com/video/BV1xa4y1v7rR
GitHub原始碼:https://github.com/hllive/LearnEFCore3.1
預加載(Eager Loading)
把所有俱樂部和俱樂部的聯賽全部查詢出來,使用Include()方法關聯起來
[HttpGet("Eager")]
public IActionResult GetEager()
{
var clubs = _dbContext.Clubs
.Include(c => c.League)//關聯資料
.ToList();
return Ok(clubs);
}
生成的SQL陳述句

執行結果

這樣查詢所有資料是沒有意義的,我們可以添加過濾條件,過濾條件在DbSet()后添加Where()方法;
[HttpGet("Eager")]
public IActionResult GetEager()
{
var clubs = _dbContext.Clubs
.Where(c => c.Name.Contains("足球隊"))//過濾條件
.Include(c => c.League)//關聯資料
.ToList();
return Ok(clubs);
}
注意!不能將ToList()或FirstDefault()放在Include()的前面,Include()只針對IQueryable回傳型別才有Include()方法;另外DbSet()的Find()方法也不支持Include()
現在可以把俱樂部的所有隊員加載進來,每個隊員也有簡歷關聯,關聯簡歷使用ThenInclude()方法(級聯添加關系資料)
Include是針對Clubs的關聯,子屬性需要關聯就使用ThenInclude()方法
如果隊員還需要關聯GamePlayer的話,再使用ThenInclude()方法是不行的,在繼續使用ThenInclude()方法的話是針對Resume的關聯,
[HttpGet("Eager")]
public IActionResult GetEager()
{
var clubs = _dbContext.Clubs
.Where(c => c.Name.Contains("足球隊")) //過濾條件
.Include(c => c.League) //關聯資料-聯賽
.Include(c => c.Players) //關聯資料-隊員
.ThenInclude(p => p.Resume) //關聯子屬性的簡歷
.Include(c => c.Players) //繼續關聯資料-隊員
.ThenInclude(p => p.GamePlayers) //關聯子屬性
.ThenInclude(g => g.Game) //GamePlayers關聯Game子屬性
.ToList();
return Ok(clubs);
}
最終生成的SQL陳述句
SELECT [c].[Id], [c].[City], [c].[DateOfEstablishment], [c].[History], [c].[LeagueId], [c].[Name], [l].[Id], [l].[Country], [l].[Name], [t0].[Id], [t0].[Birth], [t0].[ClubId], [t0].[Name], [t0].[ResumeId], [t0].[Id0], [t0].[Description], [t0].[PlayerId], [t0].[PlayerId0], [t0].[GameId], [t0].[Id1], [t0].[Round], [t0].[StartTime]
FROM [Clubs] AS [c]
LEFT JOIN [Leagues] AS [l] ON [c].[LeagueId] = [l].[Id]
LEFT JOIN (
SELECT [p].[Id], [p].[Birth], [p].[ClubId], [p].[Name], [p].[ResumeId], [r].[Id] AS [Id0], [r].[Description], [r].[PlayerId], [t].[PlayerId] AS [PlayerId0], [t].[GameId], [t].[Id] AS [Id1], [t].[Round], [t].[StartTime]
FROM [Players] AS [p]
LEFT JOIN [Resumes] AS [r] ON [p].[Id] = [r].[PlayerId]
LEFT JOIN (
SELECT [g].[PlayerId], [g].[GameId], [g0].[Id], [g0].[Round], [g0].[StartTime]
FROM [GamePlayers] AS [g]
INNER JOIN [Games] AS [g0] ON [g].[GameId] = [g0].[Id]
) AS [t] ON [p].[Id] = [t].[PlayerId]
) AS [t0] ON [c].[Id] = [t0].[ClubId]
WHERE CHARINDEX(N'足球隊', [c].[Name]) > 0
ORDER BY [c].[Id], [t0].[Id], [t0].[PlayerId0], [t0].[GameId], [t0].[Id1]
最終查詢出來的資料
[
{
"id": "21ec89ad-1b66-4b65-03e4-08d845203d1f",
"name": "茅臺足球隊",
"city": "貴州仁懷",
"dateOfEstablishment": "1999-07-01T00:00:00",
"history": null,
"league": {
"id": "edaaee79-78c9-43b5-a924-08d845203d11",
"name": "遵義仁懷足球聯賽",
"country": null
},
"players": []
},
{
"id": "5d711109-2807-472d-6c10-08d847725f25",
"name": "新智聯足球隊",
"city": "貴州省貴陽市",
"dateOfEstablishment": "2020-08-23T00:00:00",
"history": "參加很多比賽",
"league": {
"id": "4227506d-05e4-47a2-b94f-08d8451d5dc0",
"name": "第一季度足球聯賽-",
"country": "中國"
},
"players": [
{
"id": "fa896d64-e87c-4087-4e18-08d847725f2b",
"name": "王建國",
"birth": "1994-08-02T00:00:00",
"gamePlayers": [],
"resumeId": "00000000-0000-0000-0000-000000000000",
"resume": null
},
{
"id": "916ea175-5aa9-4249-4e19-08d847725f2b",
"name": "李剛",
"birth": "1994-09-25T00:00:00",
"gamePlayers": [],
"resumeId": "00000000-0000-0000-0000-000000000000",
"resume": null
},
{
"id": "3fed8a06-6e9a-461e-2249-08d847cd3d8b",
"name": "陳浩杰",
"birth": "2000-05-06T00:00:00",
"gamePlayers": [],
"resumeId": "00000000-0000-0000-0000-000000000000",
"resume": null
}
]
}
]
繼續看例子,通過Linq運算式,相當于SQL陳述句的寫法,選擇不同的欄位;使用Select()方法選擇一些欄位,使用匿名類,匿名類中包括自己的一些屬性和關聯屬性的屬性
[HttpGet("Eager2")]
public IActionResult GetEager2()
{
var clubs = _dbContext.Clubs
.Where(c => c.Name.Contains("足球隊")) //過濾條件
.Select(x => new
{
x.Id,//自己的屬性
x.Name,//自己的屬性
LeagueName = x.League.Name,//關聯屬性的屬性
Players = x.Players.Where(p => p.Birth > new DateTime(2000, 1, 1))//查詢過濾條件的隊員
})
.ToList();
return Ok(clubs);
}
生成的SQL陳述句
SELECT [c].[Id], [c].[Name], [l].[Name], [t].[Id], [t].[Birth], [t].[ClubId], [t].[Name], [t].[ResumeId]
FROM [Clubs] AS [c]
LEFT JOIN [Leagues] AS [l] ON [c].[LeagueId] = [l].[Id]
LEFT JOIN (
SELECT [p].[Id], [p].[Birth], [p].[ClubId], [p].[Name], [p].[ResumeId]
FROM [Players] AS [p]
WHERE [p].[Birth] > '2000-01-01'
) AS [t] ON [c].[Id] = [t].[ClubId]
WHERE CHARINDEX(N'足球隊', [c].[Name]) > 0
ORDER BY [c].[Id], [t].[Id]
查詢出來的資料
[
{
"id": "21ec89ad-1b66-4b65-03e4-08d845203d1f",
"name": "茅臺足球隊",
"leagueName": "遵義仁懷足球聯賽",
"players": []
},
{
"id": "5d711109-2807-472d-6c10-08d847725f25",
"name": "新智聯足球隊",
"leagueName": "第一季度足球聯賽-",
"players": [
{
"id": "3fed8a06-6e9a-461e-2249-08d847cd3d8b",
"name": "陳浩杰",
"birth": "2000-05-06T00:00:00",
"gamePlayers": [],
"resumeId": "00000000-0000-0000-0000-000000000000",
"resume": null
}
]
}
]
這種查詢出來的結果是一個匿名類,匿名類Context不能進行變化追蹤,只能追蹤它識別的類=>DbSet
但是匿名類中包括Context識別的類,上例子中的Players類是可以被Context識別并追蹤的,這種也可以進行變化追蹤操作,
顯式加載(Explicit Loading)
通過物件逐一查詢關聯資料
[HttpGet("Explicit")]
public IActionResult GetExplicit()
{
//1、查詢一條俱樂部資料
var club = _dbContext.Clubs.FirstOrDefault();
//2、通過查詢出來的物件逐一查詢關聯資料-隊員
_dbContext.Entry(club)
.Collection(x => x.Players)//關聯隊員集合資料
.Load();
//3、通過查詢出來的物件逐一查詢關聯資料-聯賽
_dbContext.Entry(club)
.Reference(x => x.League)//關聯單個聯賽資料
.Load();
return Ok(club);
}

從結果中看出執行了3次SQL陳述句查詢
這種方法只能針對單個資料進行逐一查詢關聯資料,也就是單個俱樂部,如果針對list集合這種方法就不行,
也可以給查詢陳述句添加過濾條件
[HttpGet("Explicit")]
public IActionResult GetExplicit()
{
//1、查詢一條俱樂部資料
var club = _dbContext.Clubs.FirstOrDefault();
//2、通過查詢出來的物件逐一查詢關聯資料-隊員
_dbContext.Entry(club)
.Collection(x => x.Players)//關聯隊員集合資料
.Query().Where(x => x.Birth > new DateTime(2000, 1, 1))//添加過濾條件
.Load();
//3、通過查詢出來的物件逐一查詢關聯資料-聯賽
_dbContext.Entry(club)
.Reference(x => x.League)//關聯單個聯賽資料
.Load();
return Ok(club);
}
懶加載(Lazy Loading)
這種特性在EFCore中默認是關閉的,懶加載會遇到很多問題
其他查詢
1、使用關聯物件的一些屬性作為查詢過濾條件,以下是通過俱樂部關聯的聯賽按聯賽的過濾查詢條件獲取資料
var data = https://www.cnblogs.com/hllive/p/_dbContext.Clubs.Where(x => x.League.Name.Contains("足球聯賽"));
2、查詢多對多關系
由于GamePlayer沒在Context的DbSet屬性里,但實際是被追蹤的,可以使用context.set<GamePlayer>(),如果遇到在Context的DbSet屬性沒有體現的類,可以使用context.set<class>()追蹤
博客文章可以轉載,但不可以宣告為原創
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/74.html
