Entity Framework Add()写入Oracle数据库时触发虚拟列插入禁用错误
我有一个Oracle数据库,包含多张表(其中3张表的ID列配置为ALWAYS GENERATED IDENTITY),需连接至Windows Form程序。
BREAKDOWN表结构
CREATE TABLE "BREAKDOWN" ("EQUIPMENTID" NCHAR(8) NOT NULL ENABLE, "FROMTIME" TIMESTAMP (4), "TOTIME" TIMESTAMP (4), "WORKORDER" NUMBER(10,0) NOT NULL ENABLE, "BDNREASON" NCHAR(10), "BDNTIME" INTERVAL DAY (2) TO SECOND (6) GENERATED ALWAYS AS (("TOTIME"-"FROMTIME")DAY(9) TO SECOND(4)) VIRTUAL , "BREAKDOWNID" NUMBER(10,0) GENERATED ALWAYS AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE, CONSTRAINT "BREAKDOWN_PK" PRIMARY KEY ("BREAKDOWNID") )
BREAKDOWN实体类
public partial class BREAKDOWN { public string EQUIPMENTID { get; set; } public Nullable<System.DateTime> FROMTIME { get; set; } public Nullable<System.DateTime> TOTIME { get; set; } public int WORKORDER { get; set; } public string BDNREASON { get; set; } public int BREAKDOWNID { get; set; } public Nullable<decimal> BDNTIME { get; set; } public virtual EQUIPMENT EQUIPMENT { get; set; } public virtual WORKORDER WORKORDER1 { get; set; } }
WORKORDER表结构
CREATE TABLE "WORKORDER" ( "WORKORDERTYPEID" NCHAR(3) NOT NULL ENABLE, "WORKORDEREQUIPMENTID" NCHAR(8) NOT NULL ENABLE, "WORKORDERREGISTERATIONDATE" TIMESTAMP (3) NOT NULL ENABLE, "WORKORDERID" NUMBER(10,0) GENERATED ALWAYS AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 NOCACHE NOORDER NOCYCLE NOT NULL ENABLE, CONSTRAINT "WORKORDER_PK" PRIMARY KEY ("WORKORDERID") )
WORKORDER实体类
public partial class WORKORDER { public WORKORDER() { this.BREAKDOWNs = new HashSet<BREAKDOWN>(); } public string WORKORDERTYPEID { get; set; } public string WORKORDEREQUIPMENTID { get; set; } public System.DateTime WORKORDERREGISTERATIONDATE { get; set; } public int WORKORDERID { get; set; } }
错误现象
使用Oracle SQL Developer手动插入数据时,三张表的IDENTITY功能均正常。但通过Windows Form使用Entity Framework的Add()方法插入数据时,仅BREAKDOWN表触发如下错误:
System.Data.Entity.Infrastructure.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.' UpdateException: An error occurred while updating the entries. See the inner exception for details. OracleException: ORA-54013: INSERT operation disallowed on virtual columns ORA-06512: at line 4
插入代码
BREAKDOWN插入代码(报错)
BREAKDOWN newBDN = new BREAKDOWN(); newBDN.WORKORDER = workOrder.WORKORDERID; newBDN.EQUIPMENTID = workOrder.WORKORDEREQUIPMENTID; newBDN.FROMTIME = bdnFromDateTimePicker.Value; home.dbContext.BREAKDOWNs.Add(newBDN); home.dbContext.SaveChanges();
WORKORDER插入代码(正常)
WORKORDER newWorkOrder= new WORKORDER (); newWorkOrder.EQUIPMENT = (EQUIPMENT) equipmentBindingSource.DataSource; newWorkOrder.WORKORDERREGISTERATIONDATE = regsiterationDatePicker.Value; newWorkOrder.WORKORDERTYPE = (WORKORDERTYPE)workTypeTextBox.SelectedItem; home.dbContext.WORKORDERs.Add(newWorkOrder); home.dbContext.SaveChanges();
已确认:
- 相关列均为带对应ISEQ的IDENTITY列;
- 数据库表与实体类的所有列数据类型一致;
- Oracle可正确计算IDENTITY属性(通过Oracle SQL Developer手动插入验证)。
错误根源是EF尝试向Oracle的**虚拟列(BDNTIME)**执行插入操作,而Oracle禁止对虚拟列进行INSERT/UPDATE操作。WORKORDER表没有虚拟列,所以插入正常。
方法1:使用DataAnnotations标记虚拟列为计算列
修改BREAKDOWN实体类的BDNTIME属性,添加[DatabaseGenerated(DatabaseGeneratedOption.Computed)]特性,告诉EF该列由数据库生成,不需要在插入/更新时处理:
using System.ComponentModel.DataAnnotations.Schema; // 需要引入此命名空间 public partial class BREAKDOWN { // ... 其他属性 ... [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public Nullable<decimal> BDNTIME { get; set; } // ... 导航属性 ... }
方法2:使用Fluent API配置
如果不想修改实体类,可以在DbContext的OnModelCreating方法中配置该属性:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<BREAKDOWN>() .Property(b => b.BDNTIME) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed); // ... 其他实体配置 ... }
额外检查
同时确认BREAKDOWN的IDENTITY列(BREAKDOWNID)也正确配置为数据库生成:
// DataAnnotations方式 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int BREAKDOWNID { get; set; } // 或Fluent API方式 modelBuilder.Entity<BREAKDOWN>() .Property(b => b.BREAKDOWNID) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
完成配置后,EF会自动跳过对BDNTIME列的插入操作,仅处理用户指定的列,即可解决ORA-54013错误。
内容的提问来源于stack exchange,提问作者Mohamed Reyad

