Devart.Data.Oracle.EFCore - 如何使用sequence设置PK列值?
Devart.Data.Oracle.EFCore - How to use sequence to set PK column value?
我正在使用 Entity Framework Core 2.1.4 与 Oracle 11 数据库和 Devart.Data.Oracle.EFCore 提供程序。
数据库优先方法。
我想在插入时从 ID 列(主键)的序列值中获取,而不是每次都显式设置。
因此,基于与 SQL 服务器的类似信息,我按以下方式进行了操作:
实体
public class Foo
{
public int Id { get; set; }
public double Value { get; set; }
}
映射(OnModelCreating 方法)
modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
.StartsAt(1)
.IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
增值:
using (var dbContext = new FooDbContext())
{
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
在保存更改时:
OracleException: ORA-01400: cannot insert NULL into ("SCHEMA"."FOOS"."ID")
我也记录了 EF 查询。可以看到insert:
里面没有ID列
INSERT INTO SCHEMA.FOOS (VALUE)
VALUES (:p0)
我试图简单地使用 SEQ_FOO.NEXTVAL 而不是完整的 select 或默认的 EF 方法(如 HasDefaultValueSql),但没有任何效果。即使我输入:
ForOracleHasDefaultValueSql("asdasd");
这没有错误 - 只有与上述相同的异常。似乎 EF 从未调用过 SQL.
我是不是漏掉了什么重要的东西?或者可能是 Devart 内部问题?
映射:
private void FooMapping(ModelBuilder modelBuilder)
{
//modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
// .StartsAt(1)
// .IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
//entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
}
代码:
// https://www.devart.com/dotconnect/oracle/docs/?dbmonitor.html
var monitor = new OracleMonitor() { IsActive = true };
using (var dbContext = new FooModel())
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
检查在 dbMonitor 中生成的 SQL。这是你需要的吗?
好的,我有办法。看来我们需要使用 ValueGenerator。下面是我的实现。
映射
entity.Property(e => e.Id)
.ForOracleHasColumnName("ID")
.IsRequired()
.ValueGeneratedOnAdd()
.HasValueGenerator((_, __) => new SequenceValueGenerator(_defaultSchema, "SEQ_FOOS"));
SequenceValueGenerator(请注意,ValueGenerator 是 EF Core 类型)
internal class SequenceValueGenerator : ValueGenerator<int>
{
private string _schema;
private string _sequenceName;
public SequenceValueGenerator(string schema, string sequenceName)
{
_schema = schema;
_sequenceName = sequenceName;
}
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
{
using (var command = entry.Context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT {_schema}.{_sequenceName}.NEXTVAL FROM DUAL";
entry.Context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
reader.Read();
return reader.GetInt32(0);
}
}
}
}
它似乎符合我的需要。
没弄明白。我在Oracle 18C上有类似的问题-我需要在table中填写PK,PK是一个NUMBER,而不是IDENTITY(这显然是一个缺陷,稍后会更改,但现在我必须处理这个问题,因为我无权更改数据库结构,但我需要准备 CRUD 演示)。我不想使用某些 C# 值生成器,而是使用数据库补救措施。所以我尝试使用以下(但它不起作用 - 表达式被忽略):
b.HasKey(x => x.Id);
b.Property(x => x.Id).HasColumnName("C_LICENCE").IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("select round(dbms_random.value(100000, 999999)) from dual");
我怀疑这可能是因为 int 主列永远不会为空 :) 但无论如何,我需要以某种方式强制它始终通过 SQL 生成。
我正在使用 Entity Framework Core 2.1.4 与 Oracle 11 数据库和 Devart.Data.Oracle.EFCore 提供程序。 数据库优先方法。
我想在插入时从 ID 列(主键)的序列值中获取,而不是每次都显式设置。 因此,基于与 SQL 服务器的类似信息,我按以下方式进行了操作:
实体
public class Foo
{
public int Id { get; set; }
public double Value { get; set; }
}
映射(OnModelCreating 方法)
modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
.StartsAt(1)
.IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
增值:
using (var dbContext = new FooDbContext())
{
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
在保存更改时:
OracleException: ORA-01400: cannot insert NULL into ("SCHEMA"."FOOS"."ID")
我也记录了 EF 查询。可以看到insert:
里面没有ID列INSERT INTO SCHEMA.FOOS (VALUE)
VALUES (:p0)
我试图简单地使用 SEQ_FOO.NEXTVAL 而不是完整的 select 或默认的 EF 方法(如 HasDefaultValueSql),但没有任何效果。即使我输入:
ForOracleHasDefaultValueSql("asdasd");
这没有错误 - 只有与上述相同的异常。似乎 EF 从未调用过 SQL.
我是不是漏掉了什么重要的东西?或者可能是 Devart 内部问题?
映射:
private void FooMapping(ModelBuilder modelBuilder)
{
//modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
// .StartsAt(1)
// .IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
//entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
}
代码:
// https://www.devart.com/dotconnect/oracle/docs/?dbmonitor.html
var monitor = new OracleMonitor() { IsActive = true };
using (var dbContext = new FooModel())
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
检查在 dbMonitor 中生成的 SQL。这是你需要的吗?
好的,我有办法。看来我们需要使用 ValueGenerator。下面是我的实现。
映射
entity.Property(e => e.Id)
.ForOracleHasColumnName("ID")
.IsRequired()
.ValueGeneratedOnAdd()
.HasValueGenerator((_, __) => new SequenceValueGenerator(_defaultSchema, "SEQ_FOOS"));
SequenceValueGenerator(请注意,ValueGenerator 是 EF Core 类型)
internal class SequenceValueGenerator : ValueGenerator<int>
{
private string _schema;
private string _sequenceName;
public SequenceValueGenerator(string schema, string sequenceName)
{
_schema = schema;
_sequenceName = sequenceName;
}
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
{
using (var command = entry.Context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT {_schema}.{_sequenceName}.NEXTVAL FROM DUAL";
entry.Context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
reader.Read();
return reader.GetInt32(0);
}
}
}
}
它似乎符合我的需要。
没弄明白。我在Oracle 18C上有类似的问题-我需要在table中填写PK,PK是一个NUMBER,而不是IDENTITY(这显然是一个缺陷,稍后会更改,但现在我必须处理这个问题,因为我无权更改数据库结构,但我需要准备 CRUD 演示)。我不想使用某些 C# 值生成器,而是使用数据库补救措施。所以我尝试使用以下(但它不起作用 - 表达式被忽略):
b.HasKey(x => x.Id);
b.Property(x => x.Id).HasColumnName("C_LICENCE").IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("select round(dbms_random.value(100000, 999999)) from dual");
我怀疑这可能是因为 int 主列永远不会为空 :) 但无论如何,我需要以某种方式强制它始终通过 SQL 生成。