本文章是根據 微軟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); }
執行結果
博客文章能夠轉載,但不能夠聲明爲原創