ef core 6中oracle必须用hassequence替代useidentitycolumn,因后者在oracle provider中不支持;需显式声明序列、绑定默认值sql,并在触发器中处理new.id=0场景,同时手写带nocache的序列语句确保主键连续。

EF Core 6中HasSequence和UseIdentityColumn不能混用
Oracle不支持SQL Server风格的IDENTITY列语义,EF Core 6的UseIdentityColumn()在Oracle Provider里是空实现(或直接抛异常),强行调用会导致迁移失败或运行时报NotSupportedException。必须改用HasSequence显式声明序列,并手动绑定到主键。
常见错误现象:dotnet ef migrations add Init生成的SQL里出现GENERATED ALWAYS AS IDENTITY,但目标Oracle版本是11g或未启用12c+标识列特性;或迁移执行时提示“ORA-00907: missing right parenthesis”——本质是Provider把SQL Server语法错译成了Oracle不识别的结构。
- Oracle 12c+才原生支持
GENERATED ... AS IDENTITY,且需确认数据库兼容性级别 ≥ 12.1 - EF Core Oracle Provider(如Devart、Oracle.EntityFrameworkCore)对
UseIdentityColumn的支持极不稳定,多数仅适配于Oracle 19c+且需额外配置UseOracleSQLCompatibility("19c") - 生产环境建议统一走
HasSequence+HasDefaultValueSql路径,兼容性覆盖Oracle 10g~23c
如何用HasSequence正确绑定主键并避免EF默认值干扰
EF Core会为int主键属性自动赋初值0,若不干预,插入时传入ID = 0,触发器无法触发(因WHEN (NEW.ID IS NULL)不成立),最终仍报ORA-00001唯一约束冲突。
关键操作分两步:先在OnModelCreating中定义序列并设为主键默认值来源;再确保实体类主键属性不设[DatabaseGenerated(DatabaseGeneratedOption.None)],否则EF会跳过默认值逻辑。
- 在
ModelBuilder中声明序列:modelBuilder.HasSequence<int>("SEQ_USERS").StartsAt(1).IncrementsBy(1);</int> - 绑定到主键:
modelBuilder.Entity<user>().Property(e => e.Id).HasDefaultValueSql("SEQ_USERS.NEXTVAL");</user> - 实体类中保持
public int Id { get; set; },不要加init或= 0初始化 - 若用Fluent API配置主键,务必补上
.ValueGeneratedOnAdd(),否则EF认为该字段需客户端提供值
触发器必须处理NEW.ID = 0场景,不能只判IS NULL
Oracle触发器里只写WHEN (NEW.ID IS NULL)是不够的。EF Core插入时即使没显式赋值,也会把int主键传为0(.NET默认值),导致NEW.ID = 0为真,IS NULL为假,触发器被跳过。
必须把触发器条件改成WHEN (NEW.ID IS NULL OR NEW.ID = 0),或者更稳妥地用WHEN (NVL(NEW.ID, 0) = 0)。否则你看到的现象就是:第一条数据能插(因为EF第一次可能没传ID?),第二条开始全报ORA-00001。
- 标准触发器模板:
CREATE OR REPLACE TRIGGER trg_users_before_insert BEFORE INSERT ON USERS FOR EACH ROW WHEN (NEW.ID IS NULL OR NEW.ID = 0) BEGIN SELECT SEQ_USERS.NEXTVAL INTO :NEW.ID FROM DUAL; END; - 注意
:NEW.ID前的冒号不能漏,这是PL/SQL变量引用语法 - 如果表已存在数据,建序列前先查
SELECT MAX(ID) FROM USERS,让START WITH设为该值+1,避免主键冲突
迁移脚本里别信EF自动生成的CREATE SEQUENCE
EF Core迁移工具(dotnet ef migrations script)生成的CREATE SEQUENCE语句常缺NOCACHE或NOCYCLE,在RAC环境或高并发插入下可能引发序列号跳跃甚至重复——因为Oracle缓存序列值后节点宕机会丢失缓存段。
真实生产环境应手写CREATE SEQUENCE并放入自定义SQL迁移(Sql("...")),明确控制行为:
- 加
NOCACHE保证绝对连续(牺牲少量性能):CREATE SEQUENCE SEQ_USERS START WITH 1 INCREMENT BY 1 NOMAXVALUE MINVALUE 1 NOCYCLE NOCACHE; - 若允许跳跃但要高性能,用
CACHE 20,但必须同步在触发器里加异常捕获(防止NEXTVAL失败时整个事务回滚) - 别依赖迁移自动创建触发器——EF不管理触发器生命周期,删表重建后触发器不会自动恢复,必须单独维护
OR NEW.ID = 0或漏一个NOCACHE,上线就出事。











