OceanBase Connector/NET provides an EF Core Provider for OceanBase Oracle-compatible mode, implemented based on the OceanBase.ManagedDataAccess driver. It allows you to use capabilities such as OceanBase.OracleConnection and OracleCommand in EF Core to access OceanBase.
Note
In V1.3.1, independent NuGet packages for EF Core 6 and 7 are added, while the existing EF Core 8 Provider is still provided. The entry API is UseOceanBaseForOracle(). Select the corresponding Provider based on the major version of Microsoft.EntityFrameworkCore referenced by your application. Do not mix the 6/7/8 assemblies.
Install
Select the NuGet package based on the major version of your application's EF Core:
EF Core version |
NuGet Package |
|---|---|
| EF Core 6.x | OceanBase.EntityFrameworkCore6 |
| EF Core 7.x | OceanBase.EntityFrameworkCore7 |
| EF Core 8.x | OceanBase.EntityFrameworkCore8 |
# Example: EF Core 8
dotnet add package OceanBase.EntityFrameworkCore8
Connection string
Use the Oracle-compatible mode format supported by OceanBase.ManagedDataAccess. Replace the placeholders with actual environment values; do not directly copy the literals from the example:
server=<host or IP>;port=<port>;user id=<OceanBase Oracle account>;password=<password>;database=<schema name>;
If the session needs to use the GBK character set, you can append Character Set=gbk (aliases CharSet / CharacterSet):
server=<host or IP>;port=<port>;user id=<OceanBase Oracle account>;password=<password>;database=<schema name>;Character Set=gbk;
Note
user idis an OceanBase Oracle-compatible mode account.databasemust be set to the current schema. If omitted, the driver cannot open an Oracle-compatible mode connection.- The default port is typically
2881. - Starting from V1.3.0, Oracle-compatible mode supports encoding and decoding
VARCHAR2/CHAR/CLOBin GBK usingCharacter Set=gbk. If not set, it processes them as UTF-8.NCHAR/NVARCHAR2are not affected by this parameter. - When using GBK, set
Character Set=gbkin the connection string and reference the EF Core Provider of V1.3.1 or its corresponding major version. For complete parameter descriptions, see Connection strings.
Quick start
Define entities and a DbContext:
using Microsoft.EntityFrameworkCore;
using OceanBase.EntityFrameworkCore;
public sealed class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Balance { get; set; }
public DateTime CreatedTime { get; set; }
}
public sealed class AppDbContext : DbContext
{
private readonly string _connectionString;
public AppDbContext(string connectionString)
{
_connectionString = connectionString;
}
public DbSet<User> Users => Set<User>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseOceanBaseForOracle(_connectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("USERS");
entity.HasKey(x => x.Id);
entity.Property(x => x.Id)
.HasColumnName("ID");
entity.Property(x => x.Name)
.HasColumnName("NAME")
.HasMaxLength(100)
.IsRequired();
entity.Property(x => x.Balance)
.HasColumnName("BALANCE")
.HasColumnType("NUMBER(10,2)");
entity.Property(x => x.CreatedTime)
.HasColumnName("CREATED_TIME")
.HasDefaultValueSql("SYSDATE");
});
}
}
Dependency injection method:
using Microsoft.EntityFrameworkCore;
using OceanBase.EntityFrameworkCore;
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseOceanBaseForOracle(connectionString));
Complete examples
Queries, inserts, updates, and deletes:
await using var db = new AppDbContext(connectionString);
var adults = await db.Users
.Where(x => x.Balance > 100)
.OrderBy(x => x.Name)
.ToListAsync();
var user = new User
{
Name = "Alice",
Balance = 7500.50m,
CreatedTime = DateTime.Now
};
db.Users.Add(user);
await db.SaveChangesAsync();
user.Name = "Alice Zhang";
await db.SaveChangesAsync();
db.Users.Remove(user);
await db.SaveChangesAsync();
Transactions:
await using var db = new AppDbContext(connectionString);
await using var tx = await db.Database.BeginTransactionAsync();
try
{
db.Users.Add(new User { Name = "U1", Balance = 100m, CreatedTime = DateTime.Now });
db.Users.Add(new User { Name = "U2", Balance = 200m, CreatedTime = DateTime.Now });
await db.SaveChangesAsync();
await tx.CommitAsync();
}
catch
{
await tx.RollbackAsync();
throw;
}
Sample raw SQL statements
Execute DML:
var rows = await db.Database.ExecuteSqlRawAsync(
"UPDATE USERS SET BALANCE = BALANCE + 100 WHERE ID = {0}",
1);
Query scalars (ExecuteSqlRaw returns the number of affected rows and is not suitable for directly reading SELECT results):
var count = db.Database
.SqlQuery<int>($"SELECT COUNT(*) FROM USERS WHERE NAME = {"Alice"}")
.Single();
Notice
ExecuteSqlRaw is applicable to operations such as INSERT, UPDATE, and DELETE. Database.SqlQuery<T> is an API in EF Core 8. In EF Core 6/7, you can use keyless entities with FromSqlRaw or execute scalar queries via ADO.NET.
Migration and database creation
Code First migration:
dotnet ef migrations add InitialCreate
dotnet ef database update
EnsureCreated:
await db.Database.EnsureCreatedAsync();
Value generation examples
Basic value generation capabilities are currently supported, including extensions related to HiLo, Identity, and Guid.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseOceanBaseHiLo("APP_HILO_SEQ");
modelBuilder.Entity<User>(entity =>
{
entity.Property(x => x.Id)
.UseOceanBaseIdentityColumn();
});
}
CodeFirst / Data type mapping
When generating or synchronizing table schemas based on entities, the default type mapping example is as follows (subject to the actual version):
CLR Types |
Default Database Type |
|---|---|
int |
NUMBER(10) |
long |
NUMBER(19) |
short |
NUMBER(6) |
byte |
NUMBER(3) |
decimal |
DECIMAL(29,4) / NUMBER(p,s) |
double |
FLOAT(49) / BINARY_DOUBLE |
float |
BINARY_FLOAT |
string |
DefaultNVARCHAR2(Aligned with the official Oracle Provider starting from V1.3.1); ExplicitlyIsUnicode(false)TimeVARCHAR2 |
DateTime |
DATE / TIMESTAMP |
DateTimeOffset |
TIMESTAMP WITH TIME ZONE |
byte[] |
BLOB / RAW |
TimeSpan |
INTERVAL DAY TO SECOND |
For fields such as amounts and decimals, it is recommended to explicitly configure HasColumnType("NUMBER(10,2)") or HasPrecision(10, 2) to match the database table definition. If you have specific requirements for length, precision, or column type, explicitly configure them in the Fluent API or data annotations; do not rely solely on default mappings.
Considerations
1. Default string mapping
- Starting from V1.3.1, strings are mapped to
NVARCHAR2by default (to align with the official Oracle EF Provider). - When
IsUnicode(false)is explicitly configured, they are mapped toVARCHAR2.
2. decimal precision and decimal places
It is recommended to explicitly configure:
entity.Property(x => x.Balance)
.HasColumnType("NUMBER(10,2)");
or:
entity.Property(x => x.Balance)
.HasPrecision(10, 2);
3. NOT NULL string columns
The current Provider addresses the issue of null values mapped to table columns during new entity creation through the SaveChanges interceptor:
- If a default value exists, the model's default value is written first.
- If no default value exists, a required string is written as an empty space
" "(this ensures the interceptor works under Oracle's NULL semantics where an empty string represents NULL).
4. ExecuteSqlRaw and SELECT
ExecuteSqlRaw is suitable for executing INSERT / UPDATE / DELETE / DDL, but not for using SELECT COUNT(*) as a return scalar.
Correct example (EF Core 8):
var count = db.Database
.SqlQuery<int>($"SELECT COUNT(*) FROM USERS")
.Single();
ABP Framework
If your application is based on the ABP Framework (multi-tenancy, soft deletion, modular DbContext, etc.), additionally reference the OceanBase.Abp.EntityFrameworkCore ABP Provider and the EF Core Provider matching the target major version. Register AbpEntityFrameworkCoreOceanBaseModule and UseOceanBaseForOracle() in the ABP module method.
For complete integration steps, version matrix, and considerations, see Use ABP Framework.
Summary
For EF Core 6/7/8 + OceanBase Oracle-compatible mode, the current Provider supports the following main paths:
- DbContext access
- CRUD
- Transactions
- Parameterized queries
- Raw SQL
- Common LINQ translations
- Migrations / EnsureCreated
- Value generation
- scaffolding
- Execution strategies
Some APIs are limited by the main version of EF Core: EF Core 6 does not provide ExecuteUpdate / ExecuteDelete, and Database.SqlQuery<T> is an EF Core 8 API. If your business depends on JSON queries, more advanced LINQ translations, or a higher main version of EF Core, verify this separately before implementation.
