HiLo是在NHiernate中生成主鍵的一種方式,不過現在我們可以在Entity Framework Core中使用。所以在這篇內容中,我將向您在如何使用HiL在Entity Framework Core生成主鍵。 ...
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模式的優點在於您預先可以知道主鍵的值,而不用每次都與數庫據發生交互。
總結有以下四點:
- “Hi”部分由資料庫分配,兩個併發請求保證得到唯一的連續值;
- 一旦獲取“Hi”部分,我們還需要知道“incrementSize”的值(“Lo”條目的數量);
“Lo”取的範圍:[0,incrementSize]; - 標識範圍的公式是:(Hi - 1) * incrementSize) + 1 到 (Hi - 1) * incrementSize) + incrementSize)
- 當所有“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 |
關於序列更多的內容,可以查閱如下資料:
- http://www.cnblogs.com/CareySon/archive/2012/03/12/2391581.html
- http://www.cnblogs.com/dotnet261010/p/7082852.html
- http://sqlhints.com/2015/08/01/difference-between-sequence-and-identity-in-sql-server/
- https://raresql.com/2012/05/01/difference-between-identity-and-sequence/
使用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
方法定義序列的StartAt
和IncrementBy
選項,然後再使用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生成主鍵感興趣,不防自己動手測試一下。
參考資料:
- https://vladmihalcea.com/2014/06/23/the-hilo-algorithm/
- http://www.talkingdotnet.com/use-hilo-to-generate-keys-with-entity-framework-core/