Entity Framework Core 使用HiLo生成主鍵


HiLo是在NHibernate中生成主鍵的一種方式,不過現在我們可以在Entity Framework Core中使用。所以在這篇內容中,我將向您在介紹如何在Entity Framework Core中使用HiLo生成主鍵。

什么是Hilo?

HiLo是High Low的簡寫,翻譯成中文叫高低位模式。

HiLo是由“Hi”和“Lo”兩部分生成主鍵的一種模式。“Hi”部分來自數據庫,“Lo”部分在內存中生成以創建唯一值。請記住,“Lo”是一個范圍數字,如0-100。因此,當“Hi”部分用完“Lo”范圍時,再次進行數據庫調用以獲得下一個“Hi數字”。所以HiLo模式的優點在於您預先可以知道主鍵的值,而不用每次都與數庫據發生交互

總結有以下四點:

  1. “Hi”部分由數據庫分配,兩個並發請求保證得到唯一的連續值;
  2. 一旦獲取“Hi”部分,我們還需要知道“incrementSize”的值(“Lo”條目的數量);
    “Lo”取的范圍:[0,incrementSize];
  3. 標識范圍的公式是:(Hi - 1) * incrementSize) + 1(Hi - 1) * incrementSize) + incrementSize)
  4. 當所有“Lo”值使用完時,需要重新從數據庫中取出一個新的“Hi”值,並將“Lo”部分重置為0。

在這里演示在兩個並發事務中的例子,每個事務插入多個實體:

Sql Server 序列

在EF Core中使用HiLo生成主鍵,我們還需要了解Sql Server中一個概念序列(Sequence)

序列是在SQL Server 2012中引入的(不過Oracle很早就已經實現了http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_6015.htm)。序列是用戶定義的對象,它根據創建的屬性生成一系列數值。它與 Identity 列相似,但它們之間有很多不同之處。例如,

  • 序列用於生成數據庫范圍的序列號;
  • 序列不與一個表相關聯,您可以將其與多個表相關聯;
  • 它可以用於插入語句來插入標識值,也可以在T-SQL腳本中使用。

創建序列示例的SQL語句:

Create Sequence [dbo].[Sequence_Test] 
As [BigInt]         --整數類型
Start With 1        --起始值
Increment By 1      --增量值
MinValue 1          --最小值
MaxValue 9999999    --最大值
Cycle               --達到最值循環 [ CYCLE | NO CYCLE ]
Cache  5;           --每次取出5個值緩存使用 [ CACHE [<常量>] | NO CACHE ]

使用示例:

Create Table #T(Id BigInt Primary Key,[Time] DateTime);

Insert Into #T
			( Id , Time )
Values		( NEXT VALUE FOR [dbo].[Sequence_Test] , -- Id - bigint
			  GetDate()  -- Time - datetime
			  )
Go 10


Select * From #T

查詢結果:

Id Time
1 2017-11-23 16:46:50.613
2 2017-11-23 16:46:50.643
3 2017-11-23 16:46:50.667
4 2017-11-23 16:46:50.677
5 2017-11-23 16:46:50.687
6 2017-11-23 16:46:50.697
7 2017-11-23 16:46:50.707
8 2017-11-23 16:46:50.717
9 2017-11-23 16:46:50.730
10 2017-11-23 16:46:50.740

關於序列更多的內容,可以查閱如下資料:

使用HiLo生成主鍵

讓我們看看如何使用HiLo在Entity Framework Core中生成主鍵。

為了演示,我們創建了兩個沒有關系的實體。

    public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
    }
    
    public class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
    }

請記住,EF Core按慣例配置一個名為Id<type name>Id作為實體的主鍵屬性。現在我們需要創建我們的DBContext,在這里我們創建SampleDBContext.cs類:

public class SampleDBContext : DbContext
{
    public SampleDBContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
    {
            var sqlConnectionStringBuilder = new SqlConnectionStringBuilder {
                DataSource = "****",
                InitialCatalog = "EFSampleDB",
                UserID = "sa",
                Password = "***"
            };
            optionsBuilder.UseSqlServer(sqlConnectionStringBuilder.ConnectionString);

    }

    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
}
  • SampleDBContext構造函數初始化數據庫,類型於EF 6中的DropCreateDatabaseAlways
  • OnConfiguring() 方法用於配置數據庫鏈接字符串;
  • OnModelCreating方法用於定義實體模型。要定義HiLo序列,請使用ForSqlServerUseSequenceHiLo擴展方法。您需要提供序列的名稱。

運行應用程序,您應該在創建“EFSampleDB”數據庫中看到Product表、Category表和DBSequenceHiLo序列。

以下是創建DBSequenceHiLo的腳本。

Create Sequence [dbo].[DBSequenceHiLo] 
 As [BigInt]
 Start With 1
 Increment By 10
 MinValue -9223372036854775808
 MaxValue 9223372036854775807
 Cache 
Go

正如你所看到的,它從1開始,遞增是10。

現在向數據庫中添加一些數據。以下代碼首先添加3個Category實體和調用SaveChanges(),然后添加3個Product實體並調用SaveChanges()

    using (var dataContext = new SampleDBContext())
    {
        dataContext.Categories.Add(new Category() { CategoryName = "Clothing" });
        dataContext.Categories.Add(new Category() { CategoryName = "Footwear" });
        dataContext.Categories.Add(new Category() { CategoryName = "Accessories" });
        dataContext.SaveChanges();
        dataContext.Products.Add(new Product() { ProductName = "TShirts" });
        dataContext.Products.Add(new Product() { ProductName = "Shirts" });
        dataContext.Products.Add(new Product() { ProductName = "Causal Shoes" });
        dataContext.SaveChanges();
    }

當這個代碼第一次被執行,Clothing 實體通過Add方法增加到DBContext時,就會向數據庫調用獲取序列的值,我們也可以通過SQL Server Profiler來驗證它。

次調用dataContext.SaveChanges()時,3個Category實體將被保存。查看執行的SQL語句。主鍵值已經被生成,序列值的獲取也只執行了一次。

即使插入3個Product實體,序列值也不會從數據庫中獲取。只有當插入10條記錄(Lo部分耗盡)時,才會向數據庫調用獲得下一個(Hi部分)序列值。

向HiLo運用到單個實體

上面的代碼兩個表共用一個HiLo序列。如果您只想針對一個特定的表,那么您可以使用下面的代碼。

    modelbuilder.Entity<Category>().
            Property(o => o.CategoryID).ForSqlServerUseSequenceHiLo();

這段代碼將創建一個默認名稱為“EntityFrameworkHiLoSequence”的新序列,因為沒有指定名字。您也可以定義多個HiLo序列。例如:

    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");
        modelbuilder.Entity<Category>()
                .Property(o => o.CategoryID).ForSqlServerUseSequenceHiLo();
    }

在數據庫中,將創建兩個序列。Category實體將使用EntityFrameworkHiLoSequence序號,所有其它實體使用DBSequenceHiLo序列。

配置HiLo序列

ForSqlServerHasSequence擴展方法不能更改起始值和增量值的選項。但是,有一種方法來定義這些選項。首先,使用HasSequence方法定義序列的StartAtIncrementBy選項,然后再使用ForSqlServerUseSequenceHiLo()擴展方法,要保持序列的名稱一致。例如:

    modelbuilder.HasSequence<int>("DBSequenceHiLo")
                      .StartsAt(1000).IncrementsBy(5);
    modelbuilder.ForSqlServerUseSequenceHiLo("DBSequenceHiLo");

在這種情況下,生成DBSequenceHiLo的腳本如下。

CREATE SEQUENCE [dbo].[DBSequenceHiLo] 
 AS [int]
 START WITH 1000
 INCREMENT BY 5
 MINVALUE -2147483648
 MAXVALUE 2147483647
 CACHE 
GO

所以當我們執行相同的代碼插入3個Category實體,那么主鍵的值將從1000開始。

而且由於IncrementBy選項設置為“5”,所以當在上下文中添加第6個插入時,將進行數據庫調用以獲得下一個序列值。以下是插入3個Category實體然后插入3個的Product實體時SQL Server profiler的屏幕截圖,您可以看到數據庫調用獲取序列的下一個值的次數是2次。

如果您對在Entity Framework Core中使用HiLo生成主鍵感興趣,不防自己動手測試一下。

參考資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM