<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入理解 EF Core:EF Core 寫入數(shù)據(jù)時(shí)發(fā)生了什么?

          共 10206字,需瀏覽 21分鐘

           ·

          2021-08-10 23:04

          閱讀本文大概需要 14 分鐘。

          原文:https://bit.ly/2C67m1C
          作者:Jon P Smith
          翻譯:王亮
          聲明:我翻譯技術(shù)文章不是逐句翻譯的,而是根據(jù)我自己的理解來(lái)表述的。其中可能會(huì)去除一些本人實(shí)在不知道如何組織但又不影響理解的句子。

          這是深入理解 EF Core 系列的第二篇文章。第一篇是關(guān)于 EF Core 如何從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)的;而這一篇是關(guān)于 EF Core 如何向數(shù)據(jù)庫(kù)寫入數(shù)據(jù)的。這是四種數(shù)據(jù)庫(kù)操作 CRUD(新增、讀取、更新和刪除)中的 CUD 部分。

          我假設(shè)你對(duì) EF Core 已經(jīng)有了一定的認(rèn)識(shí),但在深入學(xué)習(xí)之前,我們先來(lái)了解一下如何使用 EF Core,以確保我們已經(jīng)掌握了一些基本知識(shí)。這是一個(gè)“深入研究”的課題,所以我準(zhǔn)備大量的技術(shù)細(xì)節(jié),希望我的描述方式你能理解。

          本文是“深入理解 EF Core”系列中的第二篇。以下是本系列文章列表:

          概要

          ∮. EF Core 可以通過(guò)新的或已存在的關(guān)聯(lián)關(guān)系創(chuàng)建一個(gè)新的實(shí)體。為此,它必須以正確的順序來(lái)組織實(shí)體類,以便能夠建立各類之間的關(guān)聯(lián)。這使得開(kāi)發(fā)人員很容易寫出具有復(fù)雜關(guān)聯(lián)關(guān)系的類。

          ∮. 當(dāng)你調(diào)用 EF Core 的 Add 命令來(lái)添加一個(gè)新條目時(shí),會(huì)發(fā)生很多事情:

          • EF Core 查找添加的類和其他類的所有關(guān)聯(lián)。對(duì)于每個(gè)關(guān)聯(lián)的類,它也會(huì)判斷是否需要在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)新行,或者僅僅鏈接到數(shù)據(jù)庫(kù)中現(xiàn)有的行。

          • 它使用現(xiàn)有行的主鍵或偽主鍵為新添加的條目填充外鍵信息。

          ∮. EF Core 可以監(jiān)測(cè)你從數(shù)據(jù)庫(kù)讀取的類的屬性的變化。它通過(guò)已讀入的類的隱藏副本來(lái)實(shí)現(xiàn)這一點(diǎn)。當(dāng)你調(diào)用 SaveChanges 時(shí),它會(huì)將每個(gè)讀入的屬性值與其原始值進(jìn)行比較,并且會(huì)創(chuàng)建相應(yīng)的數(shù)據(jù)更新命令。

          ∮. EF Core 的 Remove 方法將刪除參數(shù)提供的實(shí)體類的主鍵所指向的數(shù)據(jù)行。如果被刪除的類有外鍵關(guān)聯(lián),那么數(shù)據(jù)庫(kù)會(huì)自動(dòng)進(jìn)行相關(guān)的操作(比如級(jí)聯(lián)刪除),但你可以更改刪除的規(guī)則。

          數(shù)據(jù)寫入基礎(chǔ)

          提示:如果你已經(jīng)對(duì) EF Core 有一定的了解,那么你可以跳過(guò)這一部分,這只是一個(gè)簡(jiǎn)單的 EF Core 寫入數(shù)據(jù)的例子。

          在這一節(jié)的介紹中,我將描述一下本文用到的數(shù)據(jù)庫(kù)結(jié)構(gòu),然后給出一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)寫入示例。下面是類/表的基本關(guān)系圖:

          這些表被映射到具有類似名稱的類,例如 Book、BookAuthor、Author,這些類的屬性名稱與表的字段名稱相同。由于篇幅有限,我不打算展開(kāi)來(lái)講這些類,但您可以在我的 GitHub 倉(cāng)庫(kù)[1]中查看這些類。

          和讀取數(shù)據(jù)一樣,EF Core 將數(shù)據(jù)寫入數(shù)據(jù)庫(kù)也是五部分:

          1. 數(shù)據(jù)庫(kù)服務(wù)器,如 SQL server, Sqlite, PostgreSQL…

          2. 映射到數(shù)據(jù)庫(kù)的一個(gè)類或多個(gè)類—我將它們稱為實(shí)體類

          3. 一個(gè)繼承 EF Core 的 DbContext 的類,該類包含 EF Core 的配置

          4. 一個(gè)創(chuàng)建數(shù)據(jù)庫(kù)的方法

          5. 最后,向數(shù)據(jù)庫(kù)寫入數(shù)據(jù)的命令

          下面的單元測(cè)試代碼來(lái)自我的 GitHub 創(chuàng)庫(kù)[2],展示了一個(gè)簡(jiǎn)單的示例,它從現(xiàn)有數(shù)據(jù)庫(kù)中讀取 4 個(gè) Book 實(shí)體及其關(guān)聯(lián)的 BookAuthor 和 Authors 實(shí)體。

          [Fact]
          public void TestWriteTestDataSqliteInMemoryOk()
          {
          //SETUP
          var options = SqliteInMemory.CreateOptions<EfCoreContext>();
          using (var context = new EfCoreContext(options))
          {
          context.Database.EnsureCreated();

          //ATTEMPT
          var book = new Book
          {
          Title = "Test",
          Reviews = new List<Review>()
          };
          book.Reviews.Add(new Review { NumStars = 5 });
          context.Add(book);
          context.SaveChanges();

          //VERIFY
          var bookWithReview = context.Books
          .Include(x => x.Reviews).Single()
          bookWithReview.Reviews.Count.ShouldEqual(1);
          }
          }

          現(xiàn)在,如果我們將單元測(cè)試代碼對(duì)應(yīng)到上面的 5 部分,結(jié)果是這樣的:

          1. 數(shù)據(jù)庫(kù)服務(wù)器——第 5 行:我選擇了一個(gè) Sqlite 數(shù)據(jù)庫(kù)服務(wù)器,在本例中是 SqliteInMemory.CreateOptions 方法,它使用我的一個(gè) NuGet 包 EfCore.TestSupport 創(chuàng)建了一個(gè)內(nèi)存數(shù)據(jù)庫(kù)(內(nèi)存中的數(shù)據(jù)庫(kù)對(duì)于單元測(cè)試非常有用,因?yàn)槟憧梢詾檫@個(gè)測(cè)試建立一個(gè)新的空數(shù)據(jù)庫(kù))。

          2. 實(shí)體類——和上一篇結(jié)構(gòu)差不多,但是多了一個(gè)與 Book 關(guān)聯(lián)的 Review 實(shí)體類。

          3. 一個(gè)繼承 DbContext 的類——第 6 行:EfCoreContext 類繼承了 DbContext 類并配置了從類到數(shù)據(jù)庫(kù)的映射關(guān)系(你可以在我的 GitHub 倉(cāng)庫(kù)[3] 中查看該類)。

          4. 一個(gè)創(chuàng)建數(shù)據(jù)庫(kù)的方法——第 8 行:第一次執(zhí)行時(shí),這句代碼會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù),包括創(chuàng)建正確的表、鍵、索引等。EnsureCreated 方法用于單元測(cè)試,但對(duì)于真實(shí)的應(yīng)用程序,你最好手動(dòng)執(zhí)行 EF Core 的 Migration 命令。

          5. 向數(shù)據(jù)庫(kù)寫入數(shù)據(jù)的命令——第 17 到 18 行:

            • 第 17 行:Add 方法告訴 EF Core 需要將一個(gè) Book 實(shí)體及其關(guān)系(在本例中,只是一個(gè) Review 實(shí)體)寫入數(shù)據(jù)庫(kù)。

            • 第 18 行:SaveChange 方法將在數(shù)據(jù)庫(kù)中的 Books 和 Reviews 表中創(chuàng)建新行。

          在 //VERIFY 注釋之后的最后幾行用來(lái)檢查數(shù)據(jù)是否已經(jīng)被寫入數(shù)據(jù)庫(kù)。

          在本例中,你向數(shù)據(jù)庫(kù)添加了新的記錄(SQL 的 INSERT INTO 命令)。EF Core 也可以處理更新和刪除數(shù)據(jù)庫(kù)的數(shù)據(jù),下一節(jié)介紹這個(gè)新增示例,然后介紹其他新增、更新和刪除的示例。

          寫入數(shù)據(jù)時(shí)數(shù)據(jù)庫(kù)端發(fā)生了什么

          我將從創(chuàng)建一個(gè)新的 Book 實(shí)體類和新的 Review 實(shí)體類開(kāi)始。這兩個(gè)類的關(guān)系比較簡(jiǎn)單。使用上面單元測(cè)試的例子,主要代碼如下:

          var book = new Book
          {
          Title = "Test",
          Reviews = new List<Review>()
          };
          book.Reviews.Add(new Review { NumStars = 1 });
          context.Add(book);
          context.SaveChanges();

          為了將這兩個(gè)實(shí)體添加到數(shù)據(jù)庫(kù),EF Core 需要這樣做:

          1. 確定它應(yīng)該以什么順序創(chuàng)建這些新行——在本例中,它必須在 Books 表中創(chuàng)建一行,這樣它就擁有 Books 的主鍵。

          2. 將主鍵復(fù)制到與其關(guān)聯(lián)的外鍵——在本例中,它將 Books 中的主鍵 BookId 復(fù)制到 Review 的外鍵。

          3. 復(fù)制數(shù)據(jù)庫(kù)中新創(chuàng)建的數(shù)據(jù),以便實(shí)體類正確表示數(shù)據(jù)庫(kù)的數(shù)據(jù)——在這種情況下,它必須復(fù)制 BookId 并更新 BookId 屬性,包括 Book 和 Review 實(shí)體類以及 Review 實(shí)體類的 ReviewId。

          下面我們看看上面代碼生成的 SQL 語(yǔ)句:

          -- 第一次訪問(wèn)數(shù)據(jù)庫(kù)
          SET NOCOUNT ON;
          -- 向數(shù)據(jù)庫(kù)的 Books 表生成一條新數(shù)據(jù).
          -- 數(shù)據(jù)庫(kù)生成 Books 的主鍵值
          INSERT INTO [Books] ([Description], [Title], ...)
          VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6);

          -- 返回主鍵值,檢查并確認(rèn)數(shù)據(jù)行是否已添加
          SELECT [BookId] FROM [Books]
          WHERE @@ROWCOUNT = 1 AND [BookId] = scope_identity();

          -- 第二次訪問(wèn)數(shù)據(jù)庫(kù)
          SET NOCOUNT ON;
          -- 向數(shù)據(jù)庫(kù)的 Review 表生成一條新數(shù)據(jù).
          -- 數(shù)據(jù)庫(kù)生成 Review 的主鍵值
          INSERT INTO [Review] ([BookId], [Comment], ...)
          VALUES (@p7, @p8, @p9, @p10);

          -- 返回主鍵值,檢查并確認(rèn)數(shù)據(jù)行是否已添加
          SELECT [ReviewId] FROM [Review]
          WHERE @@ROWCOUNT = 1 AND [ReviewId] = scope_identity();

          重要的一點(diǎn)是,EF Core 是按正確的順序處理實(shí)體類的,這樣它就可以填充外鍵。這是簡(jiǎn)單的例子,但我遇到一個(gè)客戶項(xiàng)目的例子是,我不得不建立一個(gè)非常復(fù)雜的數(shù)據(jù)組成的 15 個(gè)不同的實(shí)體類,一些實(shí)體類是新增,一些是更新和刪除,EF Core 通過(guò)一個(gè) SaveChanges 將把所有工作有序地完成了庫(kù)。因此,EF Core 使開(kāi)發(fā)者可以很容易地將復(fù)雜的數(shù)據(jù)寫入數(shù)據(jù)庫(kù)。

          我之所以提到這一點(diǎn),是因?yàn)槲铱吹竭^(guò)在 EF Core 代碼中,開(kāi)發(fā)人員多次調(diào)用 SaveChanges 方法來(lái)從第一個(gè)新增命令中獲得主鍵,并把它設(shè)置為相關(guān)實(shí)體的外鍵。例如:

          var book = new Book
          {
          Title = "Test"
          };
          context.Add(book);
          context.SaveChanges();
          var review = new Review { BookId = book.BookId, NumStars = 1 }
          context.Add(review);
          context.SaveChanges();

          雖然這代碼效果是一樣的,但它有一個(gè)缺陷——如果第二 SaveChanges 失敗,那么就會(huì)發(fā)生部分?jǐn)?shù)據(jù)更新到數(shù)據(jù)庫(kù)的情況。在某種情況下,這可能不是個(gè)問(wèn)題,但對(duì)于像我客戶那種需要保證數(shù)據(jù)一致的情況,就非常糟糕了。

          因此,從中得到的收獲是,您不需要將主鍵復(fù)制到外鍵中,因?yàn)槟憧梢栽O(shè)置導(dǎo)航屬性,EF Core 將為您挑選出外鍵。因此,如果你認(rèn)為需要調(diào)用兩次 SaveChanges,那么通常意味著你沒(méi)有設(shè)置正確的導(dǎo)航屬性來(lái)處理這種情況。

          寫數(shù)據(jù)時(shí) DbContext 做了什么

          在上一節(jié)中,你看到了 EF Core 在數(shù)據(jù)庫(kù)端做了什么,現(xiàn)在你要看看在 EF Core 中發(fā)生了什么。大多數(shù)情況,你不需要知道,但有時(shí)候知道這些是非常重要的。例如,你只能在 SaveChanges 之前捕獲數(shù)據(jù)的狀態(tài)。而對(duì)于自增主鍵,你只有在 SaveChanges 被調(diào)用之后才能拿到主鍵的值。

          與上一個(gè)示例相比,這個(gè)示例稍微復(fù)雜一些。在這個(gè)示例中,我想向你展示 EF Core 通過(guò)從數(shù)據(jù)庫(kù)中讀取的已有實(shí)體類的實(shí)例來(lái)處理另一個(gè)實(shí)體類的新實(shí)例。下面的代碼創(chuàng)建了一個(gè)新的 Book,但 Author 已經(jīng)在數(shù)據(jù)庫(kù)中了。代碼注明了階段 1、階段 2 和階段 3,然后我用圖表描述每個(gè)階段發(fā)生的事情。

          // 階段 1
          var author = context.Authors.First();
          var bookAuthor = new BookAuthor { Author = author };
          var book = new Book
          {
          Title = "Test Book",
          AuthorsLink = new List<BookAuthor> { bookAuthor }
          };

          // 階段 2
          context.Add(book);

          // 階段 3
          context.SaveChanges();

          接下來(lái)的三個(gè)圖向你展示了實(shí)體類及其跟蹤數(shù)據(jù)在每個(gè)階段內(nèi)發(fā)生的事情。每個(gè)圖顯示了其階段結(jié)束時(shí)的以下數(shù)據(jù):

          • 流程的每個(gè)階段中每個(gè)實(shí)例的狀態(tài)。

          • Book 和 BookAuthor 類是棕色的,表示它們是類的新實(shí)例,需要添加到數(shù)據(jù)庫(kù)中,而 Author 實(shí)體類是藍(lán)色的,表示從數(shù)據(jù)庫(kù)中讀取的實(shí)例。

          • 主鍵和外鍵旁邊的括號(hào)是其當(dāng)前的值。如果一個(gè)鍵是 (0),那么它還沒(méi)有被設(shè)值。

          • 箭頭連線連接的是從導(dǎo)航屬性到其相應(yīng)實(shí)體類。

          • 每個(gè)階段之間的變化通過(guò)粗體文本或箭頭連線的粗線顯示。

          下圖顯示了階段 1 完成后的情況。用于設(shè)置一個(gè)新的 Book 實(shí)體類(左)和一個(gè)新的 BookAuthor 實(shí)體類(中),后者將 Book 連接接到一個(gè)現(xiàn)有的 Author 實(shí)體類(右)。

          階段 1 這是調(diào)用任何 EF Core 方法之前的起點(diǎn)。

          下一個(gè)圖顯示了執(zhí)行 context.Add(book) 之后的情況。更改部分以粗體顯示。

          你可能會(huì)驚訝于執(zhí)行 Add 方法時(shí)所發(fā)生的事情。它將作為參數(shù)提供的實(shí)體的狀態(tài)設(shè)置為 Added(在本例中為 Book 實(shí)體)。然后通過(guò)導(dǎo)航屬性或外鍵值查看與該實(shí)體連接的所有實(shí)體。對(duì)于每個(gè)被連接的實(shí)體,它會(huì)執(zhí)行以下操作(注意:我不知道它們執(zhí)行的確切順序)。

          • 如果實(shí)體未被跟蹤(即其當(dāng)前狀態(tài)為 Detached),則將其狀態(tài)設(shè)置為 Added——在本例中,它是 BookAuthor 實(shí)體。

          • 它用主鍵的值填充正確的外鍵的值。如果連接的主鍵還不可用,它將為跟蹤的主鍵和外鍵數(shù)據(jù)的 CurrentValue 屬性設(shè)置一個(gè)惟一的負(fù)數(shù)。你可以在上圖中看到這一點(diǎn)。

          • 它填充當(dāng)前未設(shè)值的導(dǎo)航屬性——如上圖中所示。

          最后一個(gè)階段,即階段 3,是調(diào)用 SaveChanges 方法時(shí)發(fā)生的情況,如圖所示。

          在“寫數(shù)據(jù)時(shí)數(shù)據(jù)庫(kù)端發(fā)生了什么”一節(jié)中,數(shù)據(jù)庫(kù)更改的任何列都被復(fù)制回實(shí)體類中,以便實(shí)體與數(shù)據(jù)庫(kù)匹配。在本例中,數(shù)據(jù)更新到數(shù)據(jù)庫(kù)時(shí)會(huì)把主鍵值更新到 Book 的 BookId 和 BookAuthor 的 BookId。而且,此次數(shù)據(jù)庫(kù)寫入完成后,涉及的所有實(shí)體的狀態(tài)都會(huì)被更新為 Unchanged。

          對(duì)于上面這樣一個(gè)很長(zhǎng)的解釋,很多時(shí)候你不需要知道這些細(xì)節(jié),你只管它“工作了”就行。但是,當(dāng)某些東西不能正常工作或者想做一些復(fù)雜的事情時(shí),比如記錄實(shí)體類的更改,那么了解這個(gè)就非常有用。

          更新數(shù)據(jù)到數(shù)據(jù)庫(kù)時(shí)發(fā)生了什么

          上面的示例是關(guān)于向數(shù)據(jù)庫(kù)添加新記錄的,但是沒(méi)有進(jìn)行更新。在這一節(jié)中,我將展示當(dāng)你更新數(shù)據(jù)庫(kù)中已有的記錄時(shí)會(huì)發(fā)生什么。這里使用我上一篇文章“EF Core 讀取數(shù)據(jù)時(shí)發(fā)生了什么?”中講到的查詢例子。

          這個(gè)更新很簡(jiǎn)單,只有三行,但是它在代碼中有三個(gè)階段:讀取、更新和保存。

          var books = context.Books.ToList();
          books.First().PublishedOn = new DateTime(2020, 1, 1);
          context.SaveChanges();

          下圖展示了這三個(gè)階段:

          如你所見(jiàn),你使用的查詢類型很重要——普通查詢加載數(shù)據(jù)并把返回的實(shí)體保存一份“跟蹤快照”,返回的實(shí)體類被稱為“被跟蹤的”。如果實(shí)體沒(méi)有沒(méi)跟蹤,則無(wú)法更新它。

          注意:上一節(jié)中的 Author 實(shí)體類也是被“跟蹤”的。在這個(gè)例子中,Author 的跟蹤狀態(tài)告訴 EF Core Author 已經(jīng)在數(shù)據(jù)庫(kù)中,因此不會(huì)再次創(chuàng)建。

          因此,如果你更改了加載的跟蹤實(shí)體類中的任何屬性,那么當(dāng)你調(diào)用 SaveChanges 時(shí),它會(huì)將所有跟蹤的實(shí)體類與它們的跟蹤快照進(jìn)行比較。對(duì)于每個(gè)類,它遍歷映射到數(shù)據(jù)庫(kù)字段的所有屬性。這個(gè)過(guò)程稱為更改跟蹤,它將檢測(cè)被跟蹤實(shí)體中的每一個(gè)更改,包括 Title、PubishedOn 等非關(guān)系屬性。

          在這個(gè)簡(jiǎn)單的示例中,只有 4 個(gè) Book 實(shí)體,但在實(shí)際應(yīng)用程序中,您可能已經(jīng)加載了許多相互連接的實(shí)體類。在這一點(diǎn)上,比較階段可能需要一段時(shí)間。因此,你應(yīng)該嘗試只加載需要更改的實(shí)體類。

          注意:EF Core 有一個(gè)名為 Update 的命令,它用于更新每個(gè)屬性/列的特定情況。EF Core 會(huì)自動(dòng)跟蹤更改,默認(rèn)只更新已更改的屬性/列。

          每次更新都將創(chuàng)建一個(gè) SQL UPDATE 命令,所有這些更新都將在一個(gè) SQL 事務(wù)中執(zhí)行。使用 SQL 事務(wù)意味著所有更新都作為一個(gè)整體,如果其中任何一部分失敗,那么事務(wù)中的任何數(shù)據(jù)庫(kù)更改都會(huì)失效。

          從數(shù)據(jù)庫(kù)刪除數(shù)據(jù)時(shí)發(fā)生了什么

          CRUD 的最后一部分是 DELETE,這在某些情況很簡(jiǎn)單,你只需要調(diào)用 context.Remove()。在另一些情況它很復(fù)雜,例如,當(dāng)你刪除另一個(gè)實(shí)體類依賴的實(shí)體類時(shí)會(huì)發(fā)生什么?

          刪除映射到數(shù)據(jù)庫(kù)的實(shí)體類的方法是 Remove。舉個(gè)例子,我加載一個(gè)特定的 Book,然后刪除它。

          var book = context.Books
          .Single(p => p.Title == "Quantum Networking");
          context.Remove(book);
          context.SaveChanges();

          它的階段如下:

          1. 加載要?jiǎng)h除的 Book 實(shí)體類。這會(huì)獲取它的所有屬性數(shù)據(jù),但對(duì)于刪除,您實(shí)際上只需要實(shí)體類的主鍵。

          2. 調(diào)用 Remove 方法其實(shí)是將 Book 的狀態(tài)標(biāo)記為 Deleted。這些信息會(huì)有序地存儲(chǔ)在跟蹤快照中。

          3. 最后,SaveChanges 創(chuàng)建一個(gè) SQL DELETE 命令,該命令與任何其他數(shù)據(jù)庫(kù)更改一起發(fā)送到數(shù)據(jù)庫(kù),并且在一個(gè) SQL 事務(wù)中。

          這看起來(lái)很簡(jiǎn)單,但這里發(fā)生了一些重要的事情,從代碼看并不明顯。原來(lái)書名為“Quantum Networking”的書有其他一些實(shí)體類關(guān)聯(lián)到到它——在某個(gè)特定的測(cè)試用例中,書名為“Quantum Networking”的書關(guān)聯(lián)到以下實(shí)體類:

          • 兩個(gè) Review

          • 一個(gè) PriceOffer

          • 一個(gè) BookAuthor

          現(xiàn)在,Review、PriceOffer 和 BookAuthor 實(shí)體類只與這本書相關(guān)——我們使用術(shù)語(yǔ)叫依賴于 Book 實(shí)體類。因此,如果這本書被刪除了,那么這些 Review、PriceOffer 和所關(guān)聯(lián)的 BookAuthor 數(shù)據(jù)行也應(yīng)該被刪除。如果不刪除,那么數(shù)據(jù)庫(kù)的關(guān)聯(lián)關(guān)系就是不正確的,SQL 數(shù)據(jù)庫(kù)將拋出異常。那么,為什么做這個(gè)刪除工作?

          這里所發(fā)生的都是因?yàn)樵O(shè)置了級(jí)聯(lián)刪除,級(jí)聯(lián)刪除規(guī)則設(shè)置了 Books 表和三個(gè)依賴表之間的數(shù)據(jù)庫(kù)關(guān)系。下面是 EF Core 為創(chuàng)建 Review 表而生成的 SQL 命令的一個(gè)示例:

          CREATE TABLE [Review] (
          [ReviewId] int NOT NULL IDENTITY,
          [VoterName] nvarchar(max) NULL,
          [NumStars] int NOT NULL,
          [Comment] nvarchar(max) NULL,
          [BookId] int NOT NULL,
          CONSTRAINT [PK_Review] PRIMARY KEY ([ReviewId]),
          CONSTRAINT [FK_Review_Books_BookId] FOREIGN KEY ([BookId])
          REFERENCES [Books] ([BookId]) ON DELETE CASCADE
          );

          CONSTRAINT 語(yǔ)句部分定義了約束規(guī)則,該約束表示 Review 通過(guò) BookId 列鏈接到 Books 表中的一行。在該約束的最后,你將看到關(guān)于 DELETE 級(jí)聯(lián)的規(guī)則。它告訴數(shù)據(jù)庫(kù),如果它鏈接的書被刪除了,那么這個(gè) Review 也應(yīng)該被刪除。這意味著書的刪除是允許的,因?yàn)樗邢嚓P(guān)的行也被刪除了。

          這是非常有用的,但有時(shí)候想要更改刪除規(guī)則怎么辦?比如我決定不允許刪除客戶訂單中存在的書。為了做到這一點(diǎn),我在 DbContext 中添加了一些 EF Core 配置來(lái)改變刪除規(guī)則,如下:

          public class EfCoreContext : DbContext
          {
          private readonly Guid _userId;

          public EfCoreContext(DbContextOptions<EfCoreContext> options)
          : base(options)

          public DbSet<Book> Books
          { get; set; }
          //… 其它 DbSet<T>

          protected override void OnModelCreating(ModelBuilder modelBuilder
          {
          //… 其它代碼

          modelBuilder.Entity<LineItem>(
          )
          .HasOne(p => p.ChosenBook)
          .WithMany()
          .OnDelete(DeleteBehavior.Restrict)
          ;
          }
          }

          一旦該配置應(yīng)用到數(shù)據(jù)庫(kù),就不會(huì)生成 SQL 語(yǔ)句的 DELETE CASCADE。這意味著,如果你試圖刪除客戶訂單中的一本書,那么數(shù)據(jù)庫(kù)將返回一個(gè)錯(cuò)誤,EF Core 將把這個(gè)錯(cuò)誤變成一個(gè)異常。

          這使你對(duì)正在發(fā)生的事情有一個(gè)更深的了解,但是還有相當(dāng)多的內(nèi)容我沒(méi)有介紹(但我在我的書中介紹了)。這里有一些關(guān)于刪除我還沒(méi)有提到的事情:

          • 實(shí)體類之間可以有 required 關(guān)系(依賴關(guān)系)和 optional 關(guān)系,EF Core 為每種類型使用不同的規(guī)則。

          • EF Core 可以通過(guò)設(shè)置 DeleteBehavior 來(lái)設(shè)置級(jí)聯(lián)刪除規(guī)則,當(dāng)實(shí)體類存在循環(huán)關(guān)聯(lián)關(guān)系時(shí),可以用它避免一些錯(cuò)誤——一些數(shù)據(jù)庫(kù)在發(fā)現(xiàn)循環(huán)刪除時(shí)會(huì)拋出錯(cuò)誤。

          • 你可以在調(diào)用 Remove 方法時(shí)提供一個(gè)新的只有主鍵有值的類來(lái)刪除實(shí)體類。這在處理只返回主鍵的場(chǎng)景非常有用。

          總結(jié)

          本文我介紹了 CRUD 中的新增、更新和刪除部分,前一篇文章介紹了讀取部分。

          正如您所看到的,使用 EF Core 在數(shù)據(jù)庫(kù)中創(chuàng)建記錄很容易,但內(nèi)部很復(fù)雜。你通常不需要知道 EF Core 或數(shù)據(jù)庫(kù)中發(fā)生了什么,但了解一些細(xì)節(jié)可以讓你更好地利用 EF Core 的優(yōu)勢(shì)。

          更新也很簡(jiǎn)單——只需在你讀入的實(shí)體類中更改一個(gè)或多個(gè)屬性,當(dāng)你調(diào)用 SaveChanges 時(shí),EF Core 會(huì)找到已更改的數(shù)據(jù),并構(gòu)建 SQL 命令更新數(shù)據(jù)庫(kù)。這適用于非關(guān)系屬性(如圖 Book 的 Title 屬性)和導(dǎo)航屬性(你可以修改他們的關(guān)系)。

          最后,我們看了一個(gè)刪除案例。同樣很容易使用,但很多處理也是在背后執(zhí)行的。另外,敬請(qǐng)關(guān)注我的下一篇文章,我將討論所謂的“軟刪除”。如果你設(shè)置了一個(gè)標(biāo)志,EF Core 就不會(huì)再看到這個(gè)實(shí)體類了,它仍然在數(shù)據(jù)庫(kù)中,但它是隱藏的。

          希望本文對(duì)你有用,也希望你關(guān)注本系列的更多文章。

          祝你編程愉快!

          [1]. https://bit.ly/2MXK3ZY 
          [2]. https://bit.ly/2Yza7QQ 

          [3]. https://bit.ly/2Y0UORO







          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開(kāi)發(fā)者峰會(huì)資料PPT
          回復(fù) 【加群】加入dotnet微信交流群
          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产女人喷高潮视频免费 | 欧美女人超碰 | 一级做a爰片久久毛片A片 9 1? | 操逼视频网站免费 | kaobishipin |