Devart.Data.Oracle.EFCore-如何使用序列设置PK列值?

时间:2018-10-17 11:31:30

标签: oracle frameworks entity core devart

我正在使用带有Oracle 11数据库和Devart.Data.Oracle.EFCore提供程序的Entity Framework Core 2.1.4。 数据库优先方法。

我想在插入时从ID列(主键)的序列值中获取,而不必每次都明确地设置它。 因此,基于与SQL Server相似的信息,我这样做如下:

实体

    $current_url = home_url( add_query_arg( array(), $wp->request ) );
    wp_redirect($current_url);
    exit;

映射(OnModelCreating方法)

public class Foo
{
    public int Id { get; set; }
    public double Value { get; set; }
}

增加价值:

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();
}

我也记录了EF查询。如您所见,insert中没有ID列:

OracleException: ORA-01400: cannot insert NULL into ("SCHEMA"."FOOS"."ID")

我试图仅使用SEQ_FOO.NEXTVAL而不是完全选择或默认EF方法(例如HasDefaultValueSql),但没有任何效果。即使我输入:

INSERT INTO SCHEMA.FOOS (VALUE)
  VALUES (:p0)

此操作没有错误-仅与上述相同。似乎EF从未调用过该SQL。

我缺少重要的东西吗?还是内部Devart问题?

3 个答案:

答案 0 :(得分:1)

好的,我有解决办法。看来我们需要使用ValueGenerator。我的实现如下。

映射

entity.Property(e => e.Id)
      .ForOracleHasColumnName("ID")
      .IsRequired()
      .ValueGeneratedOnAdd()
      .HasValueGenerator((_, __) => new SequenceValueGenerator(_defaultSchema, "SEQ_FOOS"));

SequenceValueGenerator(请注意,ValueGenerator是EF核心类型)

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);
            }
        }
    }
}

它似乎可以按需工作。

答案 1 :(得分:0)

映射:

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。那是您需要的吗?

答案 2 :(得分:0)

不知道这一点。我在Oracle 18C上有类似的问题-我需要在表中填写PK,PK是一个数字,而不是IDENTITY(这显然是一个缺陷,以后会更改,但是现在我必须处理这个问题,因为我没有有权更改数据库结构,但是我需要准备CRUD演示)。我不想使用某些C#值生成器,而是使用-DB补救措施。因此,我尝试使用以下内容(但不起作用-表达式被忽略):

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主列永远不会为null :)但是无论如何,我都需要以某种方式强制始终通过SQL生成它。