九、Entity Framework Core 3.1入門教程-修改關係數據

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

一、簡單修改關係數據

查詢第一個俱樂部並把關聯的聯賽數據也查詢出來,而後修改關聯League中的Name屬性,因爲context比較智能,它會追蹤查詢出來的Club以及它關聯的數據,因此League一旦發生變化了以後會生成相應的Update語句。git

[HttpPut("UpdateLeagueForClub")]
public IActionResult UpdateLeagueForClub()
{
    var club = _dbContext.Clubs.Include(c => c.League).FirstOrDefault();
    club.League.Name = "新名稱";
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

二、修改離線關係數據

經過查詢多對多的關係數據,再修改關聯的數據github

[HttpPut("UpdatePlayerForGame")]
public IActionResult UpdatePlayerForGame()
{
    //一、經過Game查詢出多對多的GamePlayer的Player數據
    var game = _dbContext.Games
        .Include(g => g.GamePlayers)
            .ThenInclude(p => p.Player)
        .AsNoTracking()//不追蹤(離線數據)
        .FirstOrDefault();
    //二、修改Game中第一個隊員的Name屬性
    var firstPlayer = game.GamePlayers.First().Player;
    firstPlayer.Name = "李曉明";
    //三、修改隊員(離線數據須要使用Update()方法)
    _dbContext.Players.Update(firstPlayer);
    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}


從執行結果中能夠看出,在不追蹤的狀況下,使用Context的Update()方法,生成的SQL語句會把Game對象和Game對象下的全部隊員都更新一遍,
可是咱們只是想修改Game下的第一個隊員的Name屬性,EFCore卻執行了這麼多更新操做,顯然這樣是不合理的。
怎麼實現只修改一個Player對象呢?
使用Context上有個Entry()方法,而後傳入被修改的對象,Entry上有一個狀態State讓他等於EntityState.Modified;這裏就至關於手動設置他的狀態,這個時候呢,它就只會修改一條數據,也就是第一個player數據,其它關聯的數據狀態不會被修改。
sql

[HttpPut("UpdatePlayerForGame")]
public IActionResult UpdatePlayerForGame()
{
    //一、經過Game查詢出多對多的GamePlayer的Player數據
    var game = _dbContext.Games
        .Include(g => g.GamePlayers)
            .ThenInclude(p => p.Player)
        .AsNoTracking()//不追蹤(離線數據)
        .FirstOrDefault();
    //二、修改Game中第一個隊員的Name屬性
    var firstPlayer = game.GamePlayers.First().Player;
    firstPlayer.Name = "李曉明";
    //三、修改隊員(離線數據須要使用Update()方法)
    //_dbContext.Players.Update(firstPlayer);
    //四、使用EntityState.Modified手動設置修改狀態
    _dbContext.Entry(firstPlayer).State = EntityState.Modified;
    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}


根據執行結果能夠看出只修改了一條記錄數據庫

三、修改多對多的數據

一、直接傳值、將比賽和隊員關聯起來

好比想添加隊員和比賽之間的關係,假如前端傳入比賽ID和隊員ID,因爲GamePlayer在Context中沒有設置DbSet屬性,因此使用Context.Add(GamePlayer)ide

[HttpPut("GameAndPlayer")]
public IActionResult UpdateGameAndPlayer()
{
    //一、添加隊員和比賽之間的關係
    var gamePlayer = new GamePlayer
    {
        GameId = new Guid("D0E17A1A-6AC5-472E-C1D7-08D848950DDD"),//假如是前端傳入的比賽ID
        PlayerId = new Guid("FA896D64-E87C-4087-4E18-08D847725F2B")//假如是前端傳入的隊員ID
    };
    //二、將GamePlayer添加至Context中
    _dbContext.Add(gamePlayer);
    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

生成的SQL語句ui

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [GamePlayer] ([PlayerId], [GameId])
VALUES (@p0, @p1);
',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='FA896D64-E87C-4087-4E18-08D847725F2B',@p1='D0E17A1A-6AC5-472E-C1D7-08D848950DDD'

二、查詢出比賽,再添加隊員

[HttpPut("GameAddPlayer")]
public IActionResult UpdateGameAddPlayer()
{
    //一、把第一個比賽先查出來
    var game = _dbContext.Games.FirstOrDefault();
    //二、給查出來的比賽中添加一個隊員
    game.GamePlayers.Add(new GamePlayer { PlayerId = new Guid("916EA175-5AA9-4249-4E19-08D847725F2B") });//將隊員添加至比賽中
    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

四、刪除多對多關係數據

[HttpDelete("GamePlayer")]
public IActionResult DeleteGamePlayer() {
    //一、新建一個比賽和隊員直接的關係對象,好比是前段傳入的比賽ID和隊員ID
    var gamePlayer = new GamePlayer
    {
        GameId = new Guid("D0E17A1A-6AC5-472E-C1D7-08D848950DDD"),//假如是前端傳入的比賽ID
        PlayerId = new Guid("FA896D64-E87C-4087-4E18-08D847725F2B")//假如是前端傳入的隊員ID
    };
    //二、在Context中把GamePlayer刪除
    _dbContext.Remove(gamePlayer);

    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

執行生成的SQL語句3d

exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [GamePlayer]
WHERE [PlayerId] = @p0 AND [GameId] = @p1;
SELECT @@ROWCOUNT;'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='FA896D64-E87C-4087-4E18-08D847725F2B',@p1='D0E17A1A-6AC5-472E-C1D7-08D848950DDD'

五、修改一對一數據

一、修改變化追蹤的數據

[HttpPut("PlayerAddResume")]
public IActionResult UpdatePlayerAddResume() {
    //一、查詢出王建國隊員
    var player = _dbContext.Players.FirstOrDefault(p => p.Name == "王建國");
    //二、給隊員添加簡歷對象
    player.Resume = new Resume { Description = "我是一個DotNet工程師" };
    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

執行生成的SQL語句code

SELECT TOP(1) [p].[Id], [p].[Birth], [p].[ClubId], [p].[Name], [p].[ResumeId]
FROM [Players] AS [p]
WHERE [p].[Name] = N'王建國'
go
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Resumes] ([Id], [Description], [PlayerId])
VALUES (@p0, @p1, @p2);
',N'@p0 uniqueidentifier,@p1 nvarchar(200),@p2 uniqueidentifier',@p0='4AEE8A24-9803-4823-8737-08D8489C50B1',@p1=N'我是一個DotNet工程師',@p2='FA896D64-E87C-4087-4E18-08D847725F2B'
go

二、修改離線數據

使用Attach方法會判斷player主鍵是否有值,若是有值就是未修改狀態視頻

[HttpPut("PlayerAddResume2")]
public IActionResult UpdatePlayerAddResume2()
{
    //一、查詢出王建國隊員(不追蹤)
    var player = _dbContext.Players.AsNoTracking().FirstOrDefault(p => p.Name == "李剛");
    //二、給隊員添加簡歷對象
    player.Resume = new Resume { Description = "我是全棧工程師" };

    //執行數據庫操做
    _dbContext.Attach(player);
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

這個執行效果和上面實例差很少

三、修改已經存在的數據

若是再次執行如下Action會報錯,報錯緣由是李剛的簡歷在數據庫中已經存在。

[HttpPut("PlayerAddResume2")]
public IActionResult UpdatePlayerAddResume2()
{
    //一、查詢出王建國隊員(不追蹤)
    var player = _dbContext.Players.AsNoTracking().FirstOrDefault(p => p.Name == "李剛");
    //二、給隊員添加簡歷對象
    player.Resume = new Resume { Description = "我是全棧工程師" };

    //執行數據庫操做
    _dbContext.Attach(player);
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

分析
查詢出隊員數據中只包括了Players表中的數據,而沒有關聯其餘關係數據,也就是沒有關聯Resume,在player對象中Resume爲null,在第2句中新new了一個Resume,這時候EFCore認爲是給隊員添加簡歷,可是執行數據庫的時候發現已經存在簡歷數據,因此執行失敗
怎麼解決這個問題呢?其實就是在查詢隊員的時候一塊兒把Resume也查詢出來(使用Include關聯),若是隊員有簡歷數據,player對象中Resume就不爲null,在內存中就有變化追蹤player的Resume

[HttpPut("PlayerAddResume3")]
public IActionResult UpdatePlayerAddResume3()
{
    //一、查詢出王建國隊員,一塊兒把Resume也查詢出來
    var player = _dbContext.Players
                .Include(p=>p.Resume).FirstOrDefault(p => p.Name == "李剛");
    //二、給隊員添加簡歷對象,若是player中Resume已經存在,會先刪除以前的Resume在添加新對象
    player.Resume = new Resume { Description = "我是全棧工程師" };

    //執行數據庫操做
    int count = _dbContext.SaveChanges();
    return Ok(count);
}

執行結果

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

相關文章
相關標籤/搜索