OceanBase.EntityFrameworkCore8 is an EF Core provider for OceanBase Oracle mode, based on OceanBase Connector/NET. It allows you to use OceanBase.OracleConnection, OracleCommand, and other capabilities in EF Core to access OceanBase.
Note
Currently, this provider only supports EF Core 8.0.
Install
If you install it by using NuGet:
dotnet add package OceanBase.EntityFrameworkCore8
Connection string
Use the Oracle mode format supported by OceanBase.ManagedDataAccess. Replace the placeholders with the actual values in your environment. Do not directly copy the literals in the sample:
server=<host or IP address>;port=<port number>;user id=<OceanBase Oracle account>;password=<password>;database=<schema name>;
Note
user idspecifies the OceanBase Oracle account.databaseis recommended. The metadata capabilities of the current provider rely on it to identify the current schema.- The default port is
2881.
Get started
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");
});
}
}
Use dependency injection:
using Microsoft.EntityFrameworkCore;
using OceanBase.EntityFrameworkCore;
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseOceanBaseForOracle(connectionString));
Example
Query, insert, update, and delete data:
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();
Perform 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;
}
Execute DML statements
Execute DML statements:
var rows = await db.Database.ExecuteSqlRawAsync(
"UPDATE USERS SET BALANCE = BALANCE + 100 WHERE ID = {0}",
1);
Query a scalar value (the ExecuteSqlRaw method returns the number of affected rows, which is not suitable for directly reading the result of a SELECT statement):
var count = db.Database
.SqlQuery<int>($"SELECT COUNT(*) FROM USERS WHERE NAME = {"Alice"}")
.Single();
Notice
ExecuteSqlRaw is suitable for INSERT, UPDATE, and DELETE statements. To query a scalar value, use SqlQuery<T>() or the equivalent API for your version.
Migrations and database creation
Code First migrations:
dotnet ef migrations add InitialCreate
dotnet ef database update
EnsureCreated:
await db.Database.EnsureCreatedAsync();
Value generation
The provider supports value generation for 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();
});
}
Code First / Data type mapping
When you generate or synchronize a table schema based on an entity, the default type mapping is as follows (actual values may vary):
| CLR type | 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 |
Default VARCHAR2; NVARCHAR2 when Unicode is explicitly specified |
DateTime |
DATE / TIMESTAMP |
DateTimeOffset |
TIMESTAMP WITH TIME ZONE |
byte[] |
BLOB / RAW |
TimeSpan |
INTERVAL DAY TO SECOND |
For fields such as amounts and decimals, we recommend that you explicitly configure HasColumnType("NUMBER(10,2)") or HasPrecision(10, 2) and ensure that the settings 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 the default mapping.
Considerations
1. Default string mapping
- By default, strings are mapped to
VARCHAR2. - Strings are mapped to
NVARCHAR2only when you explicitly configureIsUnicode(true).
2. decimal precision and scale
We recommend that you 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 provider handles the issue of null values for string columns in newly added entities through the SaveChanges interceptor:
- If a default value is specified, the model's default value is written first.
- If no default value is specified, an empty string
""is written.
4. ExecuteSqlRaw and SELECT
The ExecuteSqlRaw method is suitable for executing INSERT, UPDATE, DELETE, and DDL statements. It is not suitable for using SELECT COUNT(*) to return a scalar value.
Correct example:
var count = db.Database
.SqlQuery<int>($"SELECT COUNT(*) FROM USERS")
.Single();
Summary
For EF Core 8.0 + OceanBase Oracle mode, the current provider supports the following main paths:
- DbContext access
- CRUD operations
- Transactions
- Parameterized queries
- Raw SQL
- Common LINQ translation
- Migrations / EnsureCreated
- Value generation
- Scaffolding
- Execution strategies
If your business requires JSON queries, more advanced LINQ translations, or EF Core 9 compatibility, verify them separately before integration.
