Entity Framework中主键id自增步长为1000而非1的问题咨询
为什么BillId列不是每次自增1而是突然跳1000?
这是SQL Server 2016及以上版本(包括你用的2017 Express)的默认特性导致的——IDENTITY_CACHE(标识缓存),具体原因和解决办法如下:
问题根源
SQL Server为了提升标识列(也就是你的BillId自增列)的生成性能,默认开启了IDENTITY_CACHE功能。它会预分配一批标识值(对于int类型的标识列,默认预分配1000个)存入缓存,每次生成新值时直接从缓存读取,无需每次访问磁盘。
但如果遇到以下情况,缓存中未使用的标识值会被丢弃:
- SQL Server服务意外重启
- 应用程序池回收(如果你的应用是ASP.NET类项目)
- 数据库发生故障转移或临时离线
比如你之前生成了9条账单(BillId 1-9),之后因某原因触发缓存丢弃,下一次生成账单时就会直接从预分配的下一批起始值(通常是1001)开始,看起来就像是步长突然变成了1000。
解决办法
根据你的业务需求,有几个可选方案:
1. 禁用IDENTITY_CACHE(彻底解决跳号)
如果业务需要严格连续的BillId,可以关闭标识缓存:
- 针对整个数据库:执行以下SQL语句:
ALTER DATABASE [你的数据库名称] SET IDENTITY_CACHE OFF; - 针对单个表:若只想针对Bills表关闭缓存,执行:
注意:执行前需确保表未被锁定,建议在低峰期或暂时停止应用后操作。ALTER TABLE [Bills] ALTER COLUMN [BillId] INT IDENTITY(1,1) WITH (IDENTITY_CACHE = OFF);
2. 接受跳号(无需修改配置)
其实SQL Server的标识列设计初衷并非保证值的连续性,只是保证唯一性。如果你的业务逻辑不依赖连续的BillId,仅需要唯一标识,完全可以忽略这个问题——跳号不会影响数据正确性,只是视觉上不连续而已。
3. 使用序列(SEQUENCE)替代标识列
如果想更灵活控制自增逻辑,可以用SQL Server的序列替代标识列:
- 首先创建序列:
CREATE SEQUENCE BillIdSequence START WITH 1 INCREMENT BY 1 CACHE 1; -- 缓存设为1,避免跳号 - 然后修改EF6的
NewBill方法,手动获取序列下一个值赋值给BillId:
同时要修改Bill实体的配置:去掉private void NewBill() { int nextBillId; using (var context = new Context()) { nextBillId = context.Database.SqlQuery<int>("SELECT NEXT VALUE FOR BillIdSequence").Single(); } var bill = new Bill { BillId = nextBillId, BillDate = DateTime.Now, InUse = true, Paid = false, TableId = _tableId, UserId = frmLogIn.LoggedInUserId }; using (var context = new Context()) { context.Bills.Add(bill); context.SaveChanges(); } }[DatabaseGenerated(DatabaseGeneratedOption.Identity)]注解,或在Fluent API中设置Property(b => b.BillId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None),让EF不再自动生成该列的值。
内容的提问来源于stack exchange,提问作者Walchand Warik
相关产品推荐
相关产品推荐

