<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【EF Core】帶主鍵實體與無主鍵實體

      上一次老周已介紹了 EF Core 框架自動發現實體和實體成員的原理。涉及到對源碼的分析,可能大伙伴們都看得氣壓升高了。故這一次老周不帶各位去分析源碼了,咱們聊一聊熟悉又陌生的關鍵詞——主鍵。說它熟悉,是因為只要咱們創建數據表,99%會用到;說它陌生,是指在 EF Core 中與主鍵相關的細節。

      Primary Key,翻譯為“主鍵”(這個翻譯老周沒意見,但 Thread 翻譯成“線程”感覺莫名其妙)。按其命名,即是一張表中主要的鍵,用于表明某行記錄在表中是唯一的。有大伙伴會說,那 Unique 約束也可以啊。是的,但還要有一個條件,就是不能為空值,所以,可以說主鍵是 UNIQUE 和 NOT NULL 的結合。

      數據表的主鍵可以是一列,也可以是多列。

      好了,概念說完了,咱們說回 EF。按照預置的約定(老周上一文中介紹),將屬性發現為主鍵的原則有:

      1、屬性名為 Id;

      2、屬性名為實體類名 + Id,如 ProductId、OrderId 等。

      最常用的類型是 int,自動增長。也可以用 GUID,GUID 屬性的類型可以定義為 Guid,也可以是 string。老周,有例子嗎?有,咱們玩幾個,咱們使用 Sqlite 數據庫來演示。

      1、創建一個控制臺應用。

      dotnet new console -n Demo -o .

      有伙伴會問:這個用 Copilot 能不能執行?可以,比如這樣:

      它生成的命令少了 -o . ,你可以手動補上。

      如果你不想它自動執行命令,那不要點“繼續”,復制命令文本后,點“取消”就好。若繼續,它會直接執行命令。

      盡管可以這樣用,但這樣做特愚蠢!你直接打個命令都比這個快了。寫實體類的時候,如果你不想重復敲 get 和 set,倒可以用它輔助。當然,VS其實也會提示的,你按個 Tab 就會生成了。這個東西雖然好用,但有時候也挺煩的,按個 Tab 就出一堆東西(如果不想禁掉它,可以按 Esc 鍵取消提示)。如果你真不想用它,可以到設置里面找到【文本編輯器】-【建議】,去掉 Inline Suggest: Enabled 的選項即可。

       

      2、定義實體類。這次咱們用一個 Pet 類,表示你家的寵物。

      public class Pet
      {
          public int id { get; set; }
          public string Name { get; set; }
          public int Age { get; set; }
          public string Cate { get; set; }
      }

      這個你倒可以用輔助工具寫。注意這里老周故意把標識屬性改為小寫,即 id,而不是 Id。待會咱們看看 EF 能不能識別。

      3、為項目添加包。

      dotnet add package Microsoft.EntityFrameworkCore
      dotnet add package Microsoft.EntityFrameworkCore.Sqlite

      4、寫數據上下文類。

      public class MyDbContext : DbContext
      {
          public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
          {
          }
          public DbSet<Pet> Pets { get; set; }
      }

      5、這一次咱們的連接字符串不在 MyDbContext 內部配置,而是外部構建 Options 來配置。

      // 創建選項類實例
      DbContextOptions<MyDbContext> options = new DbContextOptionsBuilder<MyDbContext>()
          .UseSqlite("Data Source=mydb.db")
          .Options;
      // 實例化 DbContext
      using var dc = new MyDbContext(options);

      6、這個例子中,咱們不創建數據庫,只是驗證一下,全小寫的 id 屬性是否能被識別為主鍵。

      /*
          此處我們不創建數據庫,只是看看它能不能識別出主鍵
      */
      // 獲取實體列表
      foreach (var ent in dc.Model.GetEntityTypes())
      {
          Console.Write($"表名: {ent.GetTableName()}");
          // 查找主鍵
          var rmykey = ent.FindPrimaryKey();
          if (rmykey != null)
          {
              Console.WriteLine($",主鍵: {string.Join(", ", rmykey.Properties.Select(p => p.Name))}");
          }
          // 實體中的列(屬性)
          foreach (var property in ent.GetProperties())
          {
              Console.WriteLine($"\t列名: {property.Name} 類型: {property.ClrType.Name}");
          }
      }

      dc.Model.GetEntityTypes 方法能夠返回模型中所有實體的信息。GetTableName 返回實體對應的數據表名,FindPrimaryKey 方法找出此實體類的主鍵。最后,GetProperties 方法獲取實體類屬性對應的列。

      上述代碼運行后得到的結果如下:

      表名: Pets,主鍵: id
          列名: id 類型: Int32
          列名: Age 類型: Int32
          列名: Cate 類型: String
          列名: Name 類型: String

      好,看來,小寫的 id 屬性是可以被識別為主鍵的(老周不再分析 EF Core 源代碼了,不然這博文就沒人看了,其實是通過約定實現的)。同理,我們還可以驗證一下,全小寫的 petid 能不能識別。把 Pet 類改為:

      public class Pet
      {
          public int petid { get; set; }
          ……
      }

      至少咱們知道,PetId 是肯定能被識別為主鍵的,現在驗證一下全小寫的<類名>id的屬性。再次運行程序,得到:

      表名: Pets,主鍵: petid
          列名: petid 類型: Int32
          列名: Age 類型: Int32
          列名: Cate 類型: String
          列名: Name 類型: String

      這個示例證明:Id 和 <類名>Id 都能被約定識別為主鍵,并且不區分大小寫

      那么,如果屬性的名稱不是 <類名>Id 呢,比如這樣改:

      public class Pet
      {
          public int BugId { get; set; }
          public string Name { get; set; } = string.Empty;
          public int Age { get; set; }
          public string? Cate { get; set; }
      }

      再次運行一下,結果不出所料。

      這段鳥語說了啥?它說 Pet 這廝必須定義主鍵,如果你不想要主鍵,那得明確地把實體配置為無主鍵。怎么配置為無主鍵咱們后文再說,現在先說說“預制菜”約定無法自動識別出主鍵,咱們如何手動配置。

      1、簡單做法,用特性在 BugId 屬性上批注一下。

      [PrimaryKey(nameof(BugId))]
      public class Pet
      {
          ……
      }

      這種方法最簡單,但老周個人不推薦,因為不集中配置,不好管理。當然,只是老周不推薦,沒說不可以用啊。

      2、通過 ModelBuilder 來配置,這個在 DbContext 的派生類中重寫 OnModelCreating 方法。

       protected override void OnModelCreating(ModelBuilder modelBuilder)
       {
           modelBuilder.Entity<Pet>().HasKey(x => x.BugId);
       }

      老周比較推薦這種方法,因為它把所有實體的配置全集中一處,將來有改動也好搞,也不容易忘這個忘那個的。兩種方法任選其一,不需要同時用。

      再次運行程序,看到想要的結果了。

      表名: Pets,主鍵: BugId
          列名: BugId 類型: Int32
          列名: Age 類型: Int32
          列名: Cate 類型: String
          列名: Name 類型: String

       

      有時候你可能會想:我的代碼中并不需要訪問主鍵,主鍵僅留給 EF 自己用于生成 SQL 語句,那我能不能把影子屬性作為主鍵呢?答案是 Yes 的。先簡單說說影子屬性(Shadow Property)是什么,一句話斯基:你的實體類中未定義的,但模型中定義了的屬性

      同理,你的 DbContext 子類需要重寫 OnModelCreating 方法。

       protected override void OnModelCreating(ModelBuilder modelBuilder)
       {
           // 這一行很重要
           modelBuilder.Entity<Pet>().Property(typeof(int), "HideId");
           // 設置主鍵
           modelBuilder.Entity<Pet>()
               .HasKey("HideId");
       }

      Pet 類可以去掉作為主鍵的屬性。

      public class Pet
      {
          public string Name { get; set; } = string.Empty;
          public int Age { get; set; }
          public string? Cate { get; set; }
      }

      由于影子屬性在實體類未定義,EF Core 并不能確定其類型能不能成為主鍵。因此,在定義主鍵前應該讓 EF 知道作為主鍵的影子屬性是支持的類型,如 int。

      modelBuilder.Entity<Pet>().Property(typeof(int), "HideId");

      上面的例子就是把影子屬性 HideId 作為 Pet 實體的主鍵。運行結果如下:

      表名: Pets,主鍵: HideId
          列名: HideId 類型: Int32
          列名: Age 類型: Int32
          列名: Cate 類型: String
          列名: Name 類型: String

      主鍵也可以由多個屬性(列)組成。比如,咱們讓 HideId 和 Name 組成主鍵。

       protected override void OnModelCreating(ModelBuilder modelBuilder)
       {
           // 這一行很重要
           modelBuilder.Entity<Pet>().Property(typeof(int), "HideId");
           // 設置主鍵
           modelBuilder.Entity<Pet>()
               .HasKey("HideId", nameof(Pet.Name));
       }

      得到的運行結果如下:

      表名: Pets,主鍵: HideId, Name
          列名: HideId 類型: Int32
          列名: Name 類型: String
          列名: Age 類型: Int32
          列名: Cate 類型: String

       

      下面咱們演示一下把 string 類型的屬性映射到 SQL Server 數據表的 unique identifier 列。

      1、用以下 SQL 腳本(使用的是 SQL Server)創建數據庫和數據表。

      -- 創建數據庫
      CREATE DATABASE Test;
      GO
      -- 切換到剛剛創建的數據庫
      USE Test;
      GO
      
      -- 創建表
      CREATE TABLE Productions (
          -- 這個是主鍵,插入時如果未提供值,則用 NEWID() 產生的值
          Pid UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
          -- 產品名稱
          ProdName NVARCHAR(40) NOT NULL,
          -- 生產年份
          Year INT,
          -- 產品尺寸
          Size DECIMAL(6,2),
          -- 產品顏色
          Color NVARCHAR(10),
          -- 備注
          Remark NVARCHAR(MAX)
      );

      2、創建控制臺 .NET 項目(此處省略250個字)。

      3、定義實體類(這個可以用 dotnet ef dbcontext 命令生成,不過老周一向習慣純手寫,生成的實體類有時候要回頭修改)。

      public class Production
      {
          public string Pid { get; set; } = null!;
          public string ProdName { get; set; } = string.Empty;
          public int? Year { get; set; }
          public decimal? Size { get; set; }
          public string? Color { get; set; }
          public string? Remark { get; set; }
      }

      Pid 屬性要作為主鍵用的,注意這里老周故意讓其默認值為 null,這樣在 EF 上下文添加實體時使用數據庫生成的值(否則會報錯)。null 后面有個感嘆號(!)這個可以避免編譯器的 Nullable 警告,具體情況你可以找微軟官方文檔,有詳細說明。就是微軟文檔寫得太好了,導致很多基礎知識老周都不必重復介紹了。

      4、派生 DbContext 的子類。

      public class MyContext : DbContext
      {
          public DbSet<Production> Productions { get; set; }
      
          protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
          {
              // 配置連接字符串
              optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=Test;Trusted_Connection=True");
          }
      
          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              modelBuilder.Entity<Production>(entbd =>
              {
                  entbd.Property(p => p.Pid)
                       .ValueGeneratedOnAdd();
                  // 產品名稱為必須,且有長度限制
                  entbd.Property(p => p.ProdName)
                       .IsRequired()
                       .HasMaxLength(40);
                  // 產品尺寸的精度要求
                  entbd.Property(p => p.Size)
                       .HasPrecision(6, 2);
                  // 產品顏色的長度限制
                  entbd.Property(p => p.Color)
                       .HasMaxLength(10);
                  // 主鍵
                  entbd.HasKey(p => p.Pid);
              });
          }
      }

      ModelBuilder 的 Entity 方法可以獲得一個 EntityTypeBuilder 對象(上面老周是調用了帶 Action 委托的重載,方便多次調用 EntityTypeBuilder 實例的成員)。EntityTypeBuilder 類內部封裝了 InternalEntityTypeBuilder 對象,各種配置方法實際調用了此 InternalEntityTypeBuilder 對象的成員。

      5、在 Program.cs 文件中,寫一下測試代碼。咱們向數據庫存入兩條記錄。

      // 實例化上下文
      MyContext dc = new MyContext();
      // 新建兩條記錄
      Production p1 = new()
      {
          ProdName = "五角褲",
          Year = 2025,
          Size = 67.33m,
          Color = "白色",
          Remark = "純化學合成纖維,無自然成份"
      };
      
      Production p2 = new()
      {
          ProdName = "無領襯衫",
          Year = 2025,
          Size = 47.00m,
          Color = "黃色",
          Remark = "冰絲,炎炎夏日,如同把冰塊披在身上"
      };
      
      dc.Productions.AddRange(p1, p2);
      // 保存到數據庫
      dc.SaveChanges();
      
      dc.Dispose();

      如果代碼順利運行,則數據庫中就有兩條新記錄了。

      select * from dbo.Productions

      當然了,對于 UNIQUE IDENTIFIER 類型的主鍵,.NET CLR 實體類的屬性除了可以用字符串類型,也可以用 Guid 類型。原理也是一樣的,這里老周就不演示了,相信大伙伴們都會的。

      ===========================================================================================================

       接下來看看無主鍵的實體。這個其實沒什么特別的知識要掌握的,但你得記住一條:無主鍵的實體只能 SELECT,不能用于 INSERT、UPDATE、DELETE 操作。一句話斯基總結就是:只能查詢不能更新

      咱們還是整個例子吧。

      1、用以下SQL腳本創建數據庫和數據表。

      create database DemoSome;
      GO
      
      use DemoSome;
      GO
      
      
      -- 創建表
      create table HandsomeBoys
      (
          BoyID int IDENTITY,
          [Name] NVARCHAR(25) not null,
          Age int,
          City NVARCHAR(10),
          PhoneNo NVARCHAR(11),
          Email NVARCHAR(40),
          CONSTRAINT [PK_HandsomeBoys] PRIMARY KEY CLUSTERED (BoyID ASC)
      );
      GO

      2、向數據表 INSERT 幾條數據用于測試,隨便寫,略。

      3、創建.NET控制臺應用程序,略。

      4、定義實體類(可以用 dotnet ef 工具生成,也可以純手打)。

      public class HandsomBoy
      {
          public int ID { get; set; }
          public string Name { get; set; } = string.Empty;
          public string? Email { get; set; }
          public string? City { get; set; }
          public int? Age { get; set; }
          public string? PhoneNo { get; set; }
      }

      5、寫數據庫上下文類,構建模型。

      public class DemoDB : DbContext
      {
          public DemoDB(DbContextOptions<DemoDB> options)
                  :base(options) { }
      
          public DbSet<HandsomBoy> HandsomeBoys { get; set; }
      
          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              // 無主鍵
              modelBuilder.Entity<HandsomBoy>().HasNoKey();
              // 表映射
              var entbd = modelBuilder.Entity<HandsomBoy>().ToTable("HandsomeBoys");
              // 屬性映射
              entbd.Property(p => p.ID).HasColumnName("BoyID");
              entbd.Property(p => p.Name)
                  .IsRequired()
                  .HasMaxLength(25);
              entbd.Property(p => p.City).HasMaxLength(10);
              entbd.Property(p => p.Email).HasMaxLength(40);
              entbd.Property(p => p.PhoneNo).HasMaxLength(11);
          }
      }

      注意要調用 HasNoKey 方法配置實體為無主鍵,不然會報錯。

      6、測試。

      // 配置選項
      DbContextOptions<DemoDB> options = new DbContextOptionsBuilder<DemoDB>()
              .UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=DemoSome;Trusted_Connection=True")
              // 記錄日志
              .LogTo(msg => Console.WriteLine(msg))
              .EnableSensitiveDataLogging()
              .Options;
      
      using var dc = new DemoDB(options);
      
      // 查詢數據
      var q = from b in dc.HandsomeBoys
              select b;
      
      foreach (var x in q)
      {
          Console.WriteLine($"ID={x.ID}, Name={x.Name}, Age={x.Age}, City={x.City}, Phone={x.PhoneNo}");
      }

      結果如下:

      ID=1, Name=小陳, Age=35, City=珠海, Phone=15562021200
      ID=2, Name=老周, Age=105, City=東莞, Phone=13888582588
      ID=3, Name=老丁, Age=45, City=中山, Phone=15840991234

      無主鍵實體也可以用特性批注。

      [Keyless]
      public class HandsomBoy
      {
          ……
      }

      兩種方法,二選一。

      好了,今天就聊到這兒了。

       

      posted @ 2025-07-20 22:57  東邪獨孤  閱讀(533)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 少妇被粗大的猛进出69影院| 中文字幕乱码亚洲无线三区| 国产人妻大战黑人第1集| 亚洲最大中文字幕无码网站| 麻豆精品一区二区三区蜜臀| 一本一道av无码中文字幕麻豆| 国产一区精品在线免费看| 四虎永久精品在线视频| 国内少妇人妻偷人精品| 久久久av男人的天堂| 亚洲AV毛片一区二区三区| 在线看免费无码av天堂的| 日韩人妻无码中文字幕视频| 亚洲天堂成年人在线视频| 中文字幕亚洲国产精品| 中国熟女仑乱hd| 国产精品天干天干综合网| 丰满人妻跪趴高撅肥臀| 国产成人精品亚洲午夜麻豆| 99热这里有精品| 国产日韩乱码精品一区二区| 国产精品中文字幕二区| 中文字幕亚洲人妻系列| 黑人巨大亚洲一区二区久| 狠狠色丁香婷婷亚洲综合| 亚洲国产成人久久77| 久久婷婷成人综合色| 亚洲无线码中文字幕在线| 人妻系列无码专区免费| 欧美大胆老熟妇乱子伦视频| 中文字幕亚洲人妻一区| 精品国产AV无码一区二区三区| 国产精品久久久久久亚洲色| 沈阳市| 亚洲一区久久蜜臀av| 国产无遮挡无码视频在线观看| 亚洲免费成人av一区| 国产精品一线天粉嫩av| 中文字幕人乱码中文| 国产91精品一区二区亚洲| 在线精品国产中文字幕|