八、Entity Framework Core 3.1入門教程-查詢關係數據

本文章是根據 微軟MVP solenovex(楊旭)老師的視頻教程編寫而來,再加上本身的一些理解。
視頻教程地址:https://www.bilibili.com/video/BV1xa4y1v7rR
GitHub源碼:https://github.com/hllive/LearnEFCore3.1git

預加載(Eager Loading)

把全部俱樂部和俱樂部的聯賽所有查詢出來,使用Include()方法關聯起來github

[HttpGet("Eager")]
public IActionResult GetEager()
{
    var clubs = _dbContext.Clubs
        .Include(c => c.League)//關聯數據
        .ToList();
    return Ok(clubs);
}

生成的SQL語句

執行結果
sql

這樣查詢全部數據是沒有意義的,咱們能夠添加過濾條件,過濾條件在DbSet()後添加Where()方法;json

[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()ide

如今能夠把俱樂部的全部隊員加載進來,每一個隊員也有簡歷關聯,關聯簡歷使用ThenInclude()方法(級聯添加關係數據)3d

Include是針對Clubs的關聯,子屬性須要關聯就使用ThenInclude()方法
若是隊員還須要關聯GamePlayer的話,再使用ThenInclude()方法是不行的,在繼續使用ThenInclude()方法的話是針對Resume的關聯。code

[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()方法選擇一些字段,使用匿名類,匿名類中包括本身的一些屬性和關聯屬性的屬性blog

[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()
{
    //一、查詢一條俱樂部數據
    var club = _dbContext.Clubs.FirstOrDefault();
    //二、經過查詢出來的對象逐一查詢關聯數據-隊員
    _dbContext.Entry(club)
        .Collection(x => x.Players)//關聯隊員集合數據
        .Load();
    //三、經過查詢出來的對象逐一查詢關聯數據-聯賽
    _dbContext.Entry(club)
        .Reference(x => x.League)//關聯單個聯賽數據
        .Load();
    return Ok(club);
}


從結果中看出執行了3次SQL語句查詢

這種方法只能針對單個數據進行逐一查詢關聯數據,也就是單個俱樂部,若是針對list集合這種方法就不行。

也能夠給查詢語句添加過濾條件

[HttpGet("Explicit")]
public IActionResult GetExplicit()
{
    //一、查詢一條俱樂部數據
    var club = _dbContext.Clubs.FirstOrDefault();
    //二、經過查詢出來的對象逐一查詢關聯數據-隊員
    _dbContext.Entry(club)
        .Collection(x => x.Players)//關聯隊員集合數據
        .Query().Where(x => x.Birth > new DateTime(2000, 1, 1))//添加過濾條件
        .Load();
    //三、經過查詢出來的對象逐一查詢關聯數據-聯賽
    _dbContext.Entry(club)
        .Reference(x => x.League)//關聯單個聯賽數據
        .Load();
    return Ok(club);
}

懶加載(Lazy Loading)

這種特性在EFCore中默認是關閉的,懶加載會遇到不少問題

其餘查詢

一、使用關聯對象的一些屬性做爲查詢過濾條件,如下是經過俱樂部關聯的聯賽按聯賽的過濾查詢條件獲取數據

var data = _dbContext.Clubs.Where(x => x.League.Name.Contains("足球聯賽"));

二、查詢多對多關係
因爲GamePlayer沒在Context的DbSet屬性裏,但實際是被追蹤的,可使用context.set<GamePlayer>(),若是遇到在Context的DbSet屬性沒有體現的類,可使用context.set<class>()追蹤

博客文章能夠轉載,但不能夠聲明爲原創

相關文章
相關標籤/搜索