如何从实体框架代码优先方法自动增加 oracle 表?
Posted
技术标签:
【中文标题】如何从实体框架代码优先方法自动增加 oracle 表?【英文标题】:How to auto-increment oracle table from Entity Framework code-first approach? 【发布时间】:2020-11-24 16:46:29 【问题描述】:我有下表,我刚刚添加了装饰器DatabaseGenerated
,如下所示:
public class Reference
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public decimal ReferenceId get; set;
public string AddressType get; set;
public string RefferenceType get; set;
public string RefferenceValue get; set;
[ForeignKey("Shipment")]
public decimal? TrackingNumber get; set;
public Shipment Shipment get; set;
而我最新推送到 oracle 19c 数据库的向上迁移是:
public override void Up()
DropPrimaryKey("SALOGSEARCH.References");
AlterColumn("SALOGSEARCH.References", "ReferenceId", c => c.Decimal(nullable: false, precision: 20, scale: 0, identity: true));
AddPrimaryKey("SALOGSEARCH.References", "ReferenceId");
然而,当我在添加后执行我的上下文保存时:
using (var context = new DbContext())
context.Reference.Add(shipperReference);
context.SaveChanges();
我遇到了异常
ORA-01400: 无法插入 NULL
我假设标签DatabaseGeneratedOption.Identity
会在Oracle 数据库中生成一个序列,并从next 或其他东西的默认值调用它。但事实并非如此。
我现在是否必须为每列手动创建触发器和序列?
如果是这样,那么如果我必须自己干预数据库,那么代码的意义何在。
我不确定这是否有任何影响,但我正在调整OnModelCreating
,如下所示:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.HasDefaultSchema("SALOGSEARCH");
modelBuilder.Properties<string>().Configure(s => s.HasMaxLength(400).HasColumnType("Varchar2"));
modelBuilder.Properties<decimal>().Configure(s => s.HasPrecision(20, 0).HasColumnType("Number"));
任何从 c# 方面解决此问题的指针将不胜感激。
编辑1: 在this tutorial 之后,我开始意识到模型类(表)的 Id 需要是一个 int,在 oracle 术语中它是 number(10, 0) 以便自动创建所需的序列(作为列默认调用nextVal) 在数据库端。
然而,从向上迁移来看,似乎没有任何迹象表明这个序列的创建,所以它只是让我相信这个创建发生在我知识领域之外的更深的地方。
【问题讨论】:
标识列既不被序列也不被触发器处理。 【参考方案1】:DatabaseGeneratedOption.Identity
需要一个序列或一个标识列。
从 12c 版本开始支持标识列,触发器和序列均未引用该列。它们由 Oracle 内部处理。如果出现错误
ORA-01400: 无法插入 NULL
这可能是因为您在没有明确命名列的情况下进行插入,或者因为您使用了错误的 IDENTITY 类型,或者因为您没有序列。
IDENTITY
列有这些选项
GENERATED ALWAYS
:Oracle 总是为标识列生成一个值。尝试将值插入标识列会导致错误。
GENERATED BY DEFAULT
:如果您不提供值,Oracle 会为标识列生成一个值。如果您提供一个值,Oracle 会将该值插入到标识列中。对于此选项,如果您将 NULL 值插入标识列,Oracle 将发出错误。
GENERATED BY DEFAULT ON NULL
:如果您提供 NULL 值或根本不提供值,Oracle 会为标识列生成一个值。
例子
SQL> create table t ( c1 number generated by default as identity ( start with 1 increment by 1 ) , c2 number ) ;
Table created.
SQL> insert into t ( c2 ) values ( 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
SQL> insert into t ( c1 , c2 ) values ( null , 1 ) ;
insert into t ( c1 , c2 ) values ( null , 1 )
*
第 1 行出现错误: ORA-01400: 无法将 NULL 插入 ("MY_SCHEMA"."T"."C1")
但是我可以进行插入并明确引用 IDENTITY
SQL> insert into t ( c1, c2 ) values ( 2, 2 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
2 2
SQL> insert into t ( c2 ) values ( 3 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
2 2
2 3
在你的情况下,我会使用GENERATED BY DEFAULT ON NULL
:
SQL> create table t ( c1 number generated by default on null as identity ( start with
1 increment by 1 ) , c2 number ) ;
Table created.
SQL> insert into t values ( null , 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
【讨论】:
我当然明白你写下的sql部分。但我似乎无法弄清楚我是否可以从我的模型类的 c# 属性甚至从向上迁移中执行GENERATED BY DEFAULT ON NULL
命令。
我相信你做不到。 IDENTITY
列的处理方式因数据库提供程序而异。您可以尝试使用触发器/序列组合,但无论如何DatabaseGeneratedOption
都不会创建这些组合
但这不是一些非常狭窄的用例场景。这就像最常见的用例,我不知道如何使用实体框架中的代码优先方法对其进行调整。以上是关于如何从实体框架代码优先方法自动增加 oracle 表?的主要内容,如果未能解决你的问题,请参考以下文章