EF Core操作Oracle插入生成异常语句引发PLS-00510错误求助
我在.NET 6环境中使用Entity Framework Core v7.0.11,搭配Oracle.EntityFrameworkCore v7.21.11和Oracle.ManagedDataAccess.Core v3.21.110 NuGet包,向Oracle的FRICTION表插入新数据时出现错误。
表结构
CREATE TABLE "xxx"."FRICTION" ( "TYPE" NUMBER(2,0) DEFAULT -1 NOT NULL ENABLE, "V_MIN" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "V_MAX" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "ALPHA_MIN" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "ALPHA_MAX" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "MU_VMIN_ALPHAMIN" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "MU_VMAX_ALPHAMIN" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "EXP_ALPHAMIN" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "MU_VMIN_ALPHAMAX" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "MU_VMAX_ALPHAMAX" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "EXP_ALPHAMAX" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "OILED_MU_OFFSET" FLOAT(25) DEFAULT 0 NOT NULL ENABLE, "WEAR_MAX_MU_OFFSET" FLOAT(25) DEFAULT 0.01 NOT NULL ENABLE, "WEAR_MAX_MILEAGE" FLOAT(25) DEFAULT 150 NOT NULL ENABLE, "WEAR_EXP" FLOAT(25) DEFAULT 25 NOT NULL ENABLE, "ADA_RATE" FLOAT(25) DEFAULT 0.3 NOT NULL ENABLE, "MIN_VMIN_ALPHAMIN" FLOAT(25) DEFAULT 0.045 NOT NULL ENABLE, "MAX_VMIN_ALPHAMIN" FLOAT(25) DEFAULT 0.075 NOT NULL ENABLE, "MIN_VMIN_ALPHAMAX" FLOAT(25) DEFAULT 0.065 NOT NULL ENABLE, "MAX_VMIN_ALPHAMAX" FLOAT(25) DEFAULT 0.095 NOT NULL ENABLE, "MIN_VMAX_ALPHAMIN" FLOAT(25) DEFAULT 0.025 NOT NULL ENABLE, "MAX_VMAX_ALPHAMIN" FLOAT(25) DEFAULT 0.075 NOT NULL ENABLE, "MIN_VMAX_ALPHAMAX" FLOAT(25) DEFAULT 0.065 NOT NULL ENABLE, "MAX_VMAX_ALPHAMAX" FLOAT(25) DEFAULT 0.095 NOT NULL ENABLE )
错误信息
ORA-06550: line 5, column 19:
PLS-00510: Float cannot have scale
EF Core生成的执行语句
DECLARE TYPE "rFRICTION_0" IS RECORD ( "OILED_MU_OFFSET" FLOAT(38,17) ); TYPE "tFRICTION_0" IS TABLE OF "rFRICTION_0"; "lFRICTION_0" "tFRICTION_0"; BEGIN "lFRICTION_0" := "tFRICTION_0"(); "lFRICTION_0".extend(1); INSERT INTO "AMEH_RCM"."FRICTION" ("TYPE", "ADA_RATE", "ALPHA_MAX", "ALPHA_MIN", "EXP_ALPHAMAX", "EXP_ALPHAMIN", "MAX_VMAX_ALPHAMAX", "MAX_VMAX_ALPHAMIN", "MAX_VMIN_ALPHAMAX", "MAX_VMIN_ALPHAMIN", "MIN_VMAX_ALPHAMAX", "MIN_VMAX_ALPHAMIN", "MIN_VMIN_ALPHAMAX", "MIN_VMIN_ALPHAMIN", "MU_VMAX_ALPHAMAX", "MU_VMAX_ALPHAMIN", "MU_VMIN_ALPHAMAX", "MU_VMIN_ALPHAMIN", "V_MAX", "V_MIN", "WEAR_EXP", "WEAR_MAX_MILEAGE", "WEAR_MAX_MU_OFFSET") VALUES (:p0, :p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9, :p10, :p11, :p12, :p13, :p14, :p15, :p16, :p17, :p18, :p19, :p20, :p21, :p22) RETURNING "OILED_MU_OFFSET" INTO "lFRICTION_0"(1)."OILED_MU_OFFSET"; OPEN :cur0 FOR SELECT "lFRICTION_0"(1)."OILED_MU_OFFSET" FROM DUAL; END;
业务代码
var frictionEntity = new Friction { Type = frictionSelectedItem.Type, VMin = frictionSelectedItem.VMin, VMax = frictionSelectedItem.VMax, AlphaMin = frictionSelectedItem.AlphaMin, AlphaMax = frictionSelectedItem.AlphaMax, MuVminAlphamin = frictionSelectedItem.MuVminAlphamin, MuVmaxAlphamin = frictionSelectedItem.MuVmaxAlphamin, ExpAlphamin = frictionSelectedItem.ExpAlphamin, MuVminAlphamax = frictionSelectedItem.MuVminAlphamax, MuVmaxAlphamax = frictionSelectedItem.MuVmaxAlphamax, ExpAlphamax = frictionSelectedItem.ExpAlphamax, OiledMuOffset = frictionSelectedItem.OiledMuOffset, WearMaxMuOffset = frictionSelectedItem.WearMaxMuOffset, WearMaxMileage = frictionSelectedItem.WearMaxMileage, WearExp = frictionSelectedItem.WearExp, AdaRate = frictionSelectedItem.AdaRate, MinVminAlphamin = frictionSelectedItem.MinVminAlphamin, MaxVminAlphamin = frictionSelectedItem.MaxVminAlphamin, MinVminAlphamax = frictionSelectedItem.MinVminAlphamax, MaxVminAlphamax = frictionSelectedItem.MaxVminAlphamax, MinVmaxAlphamin = frictionSelectedItem.MinVmaxAlphamin, MaxVmaxAlphamin = frictionSelectedItem.MaxVmaxAlphamin, MinVmaxAlphamax = frictionSelectedItem.MinVmaxAlphamax, MaxVmaxAlphamax = frictionSelectedItem.MaxVmaxAlphamax }; context.Frictions.Add(frictionEntity); context.SaveChanges();
更新:实体类与上下文配置
实体映射
[Table("FRICTION")] public partial class Friction { [Key] [Column("TYPE")] [Precision(2)] public int Type { get; set; } [Column("V_MIN", TypeName = "FLOAT")] public double VMin { get; set; } [Column("V_MAX", TypeName = "FLOAT")] public double VMax { get; set; } [Column("ALPHA_MIN", TypeName = "FLOAT")] public double AlphaMin { get; set; } [Column("ALPHA_MAX", TypeName = "FLOAT")] public double AlphaMax { get; set; } [Column("MU_VMIN_ALPHAMIN", TypeName = "FLOAT")] public double MuVminAlphamin { get; set; } [Column("MU_VMAX_ALPHAMIN", TypeName = "FLOAT")] public double MuVmaxAlphamin { get; set; } [Column("EXP_ALPHAMIN", TypeName = "FLOAT")] public double ExpAlphamin { get; set; } [Column("MU_VMIN_ALPHAMAX", TypeName = "FLOAT")] public double MuVminAlphamax { get; set; } [Column("MU_VMAX_ALPHAMAX", TypeName = "FLOAT")] public double MuVmaxAlphamax { get; set; } [Column("EXP_ALPHAMAX", TypeName = "FLOAT")] public double ExpAlphamax { get; set; } [Column("OILED_MU_OFFSET", TypeName = "FLOAT")] public double OiledMuOffset { get; set; } [Column("WEAR_MAX_MU_OFFSET", TypeName = "FLOAT")] public double WearMaxMuOffset { get; set; } [Column("WEAR_MAX_MILEAGE", TypeName = "FLOAT")] public double WearMaxMileage { get; set; } [Column("WEAR_EXP", TypeName = "FLOAT")] public double WearExp { get; set; } [Column("ADA_RATE", TypeName = "FLOAT")] public double AdaRate { get; set; } [Column("MIN_VMIN_ALPHAMIN", TypeName = "FLOAT")] public double MinVminAlphamin { get; set; } [Column("MAX_VMIN_ALPHAMIN", TypeName = "FLOAT")] public double MaxVminAlphamin { get; set; } [Column("MIN_VMIN_ALPHAMAX", TypeName = "FLOAT")] public double MinVminAlphamax { get; set; } [Column("MAX_VMIN_ALPHAMAX", TypeName = "FLOAT")] public double MaxVminAlphamax { get; set; } [Column("MIN_VMAX_ALPHAMIN", TypeName = "FLOAT")] public double MinVmaxAlphamin { get; set; } [Column("MAX_VMAX_ALPHAMIN", TypeName = "FLOAT")] public double MaxVmaxAlphamin { get; set; } [Column("MIN_VMAX_ALPHAMAX", TypeName = "FLOAT")] public double MinVmaxAlphamax { get; set; } [Column("MAX_VMAX_ALPHAMAX", TypeName = "FLOAT")] public double MaxVmaxAlphamax { get; set; } }
DbContext的OnModelCreating配置
modelBuilder.Entity<Friction>(entity => { entity.HasKey(e => e.Type).HasName("FRICTION_PK"); entity.Property(e => e.Type).HasDefaultValueSql("-1"); entity.Property(e => e.AlphaMax).HasDefaultValueSql("0"); entity.Property(e => e.AlphaMin).HasDefaultValueSql("0"); entity.Property(e => e.ExpAlphamax).HasDefaultValueSql("0"); entity.Property(e => e.ExpAlphamin).HasDefaultValueSql("0"); entity.Property(e => e.MuVmaxAlphamax).HasDefaultValueSql("0"); entity.Property(e => e.MuVmaxAlphamin).HasDefaultValueSql("0"); entity.Property(e => e.MuVminAlphamax).HasDefaultValueSql("0"); entity.Property(e => e.MuVminAlphamin).HasDefaultValueSql("0"); entity.Property(e => e.OiledMuOffset).HasDefaultValueSql("0"); entity.Property(e => e.VMax).HasDefaultValueSql("0"); entity.Property(e => e.VMin).HasDefaultValueSql("0"); entity.Property(e => e.WearExp).HasDefaultValueSql("25"); entity.Property(e => e.WearMaxMileage).HasDefaultValueSql("150"); entity.Property(e => e.WearMaxMuOffset).HasDefaultValueSql("0.01"); entity.Property(e => e.AdaRate).HasDefaultValueSql("0.3"); entity.Property(e => e.MinVminAlphamin).HasDefaultValueSql("0.045"); entity.Property(e => e.MaxVminAlphamin).HasDefaultValueSql("0.075"); entity.Property(e => e.MinVminAlphamax).HasDefaultValueSql("0.065"); entity.Property(e => e.MaxVminAlphamax).HasDefaultValueSql("0.095"); entity.Property(e => e.MinVmaxAlphamin).HasDefaultValueSql("0.025"); entity.Property(e => e.MaxVmaxAlphamin).HasDefaultValueSql("0.075"); entity.Property(e => e.MinVmaxAlphamax).HasDefaultValueSql("0.065"); entity.Property(e => e.MaxVmaxAlphamax).HasDefaultValueSql("0.095"); });
核心疑问
为什么EF Core会生成带RETURNING的PL/SQL块,而不是单纯的INSERT语句?而且只有当OILED_MU_OFFSET为0时才会触发这个错误?
1. 生成RETURNING语句的原因
EF Core插入数据时,若实体属性配置了数据库默认值,且代码赋值与默认值相同,EF Core无法区分该值是手动设置还是数据库默认生成,会自动生成带RETURNING子句的PL/SQL块,将数据库实际存储的值回写到实体,保证内存与数据库数据一致。
具体到你的场景:
- 当
OILED_MU_OFFSET赋值为非0时,EF Core判断是手动赋值,直接生成普通INSERT; - 当赋值为0时,EF Core无法确认是否依赖数据库默认值,触发RETURNING逻辑,生成PL/SQL块同步数据。
2. 错误根源
Oracle的PL/SQL语法中,FLOAT类型不允许指定scale(小数位数),但当前版本的Oracle EF Core驱动错误地给FLOAT加上了(38,17)的精度和小数位数,导致Oracle抛出PLS-00510错误。这是驱动的版本兼容性问题。
3. 解决方案
方案一:匹配数据库类型定义
修改实体类中OILED_MU_OFFSET的TypeName为"FLOAT(25)",和数据库表定义一致:
[Column("OILED_MU_OFFSET", TypeName = "FLOAT(25)")] public double OiledMuOffset { get; set; }
同时在OnModelCreating中同步配置:
entity.Property(e => e.OiledMuOffset) .HasDefaultValueSql("0") .HasColumnType("FLOAT(25)");
这样EF Core生成PL/SQL时会使用正确的FLOAT(25)类型,不会添加无效的scale参数。
方案二:禁用默认值回写
如果不需要EF Core同步数据库默认值到实体,可关闭该属性的默认值跟踪:
entity.Property(e => e.OiledMuOffset) .HasDefaultValueSql("0") .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
EF Core将不再生成RETURNING子句,直接使用普通INSERT语句。
方案三:升级驱动版本
该问题在Oracle.EntityFrameworkCore的更高版本中已修复,建议升级到最新稳定版(如v7.23.x及以上),避免驱动生成错误的PL/SQL类型定义。
内容的提问来源于stack exchange,提问作者dstreissi

