EF Core in progress
The Entity Framework Core provider lives in its own repository, dimzon/h2sharp-efcore — it is a fork, and its own history and release cycle are separate from the engine's.
Measurement
Run against the pinned reference engine (h2-2.5.259-SNAPSHOT.jar), not against the port — a test against the port would only prove the port agrees with itself. The suite is split by trait, and both runs are needed:
| Run | Passed | Failed | Total |
|---|---|---|---|
dotnet test --filter "Category!=BlockedByPort" — the provider is correct as far as the port allows | 1034 | 0 | 1034 |
dotnet test --filter "Category=BlockedByPort" — the port defects the provider has signed up for | 19 | 185 | 206 |
| Total | 1053 | 185 | 1240 |
A test that fails because a function was not ported stays in the suite, tagged BlockedByPort: removing it would take away the only signal that the port has not yet caught up with H2.
Using it
await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
ctx.Blogs.Add(new() { Name = "FooBlog" });
await ctx.SaveChangesAsync();
var fBlogs = await ctx.Blogs.Where(b => b.Name.StartsWith("F")).ToListAsync();
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseH2("Data Source=mem:mydatabase");
}
The connection string is the ADO.NET spelling: Data Source=mem:mydatabase is translated to jdbc:h2:mem:mydatabase, and a JDBC URL is accepted as it is.
H2's own functions
EF.Functions reaches the database's built-ins — the date/time, string, binary, number and bit ones — each checked against the engine:
EF.Functions.DateAdd("DAY", 1, blog.Created) // DATEADD('DAY', 1, ...)
EF.Functions.Locate(blog.Name, "foo") // LOCATE(...) — H2 takes the substring first
EF.Functions.Extract("YEAR", blog.Created) // YEAR(...)
EF.Functions.LeftPad(blog.Name, 10, "0") // LPAD(...)
Engine gaps the provider is waiting on
Numbered in the provider's own docs/DIVERGENCES.md, where each one says who has to fix it:
| # | Gap | Symptom |
|---|---|---|
| 1 | Quoted column reference | SELECT "e"."Id" FROM T → SessionLocal.getResolver: the query layer is not ported |
| 2 | Table alias through AS | the implicit alias (FROM T e) works; FROM T AS "e" is what the provider needs |
| 3 | GENERATED … AS IDENTITY | the parser rejects GENERATED; the object model already knows identity columns |
| 4 | FOREIGN KEY in CREATE TABLE | constraints — ROADMAP phase 7 |
| 5 | INTERVAL types | the parser does not know them; without the qualifier INTERVAL is not a type |
| 6 | DB_CLOSE_DELAY for an in-memory database | the database disappears with the last connection, so EnsureCreated cannot work |
| 7 | INFORMATION_SCHEMA | the catalogue — without it scaffolding and the schema registry are impossible |
| 8 | CREATE SEQUENCE, NEXT VALUE FOR | the parser does not know them; needed for key generation if identity does not arrive |
Also in that repository
- Plugins for
NodaTimeandNetTopologySuite, ported onto EF value converters. - Docs:
docs/DIVERGENCES.md(what differs from EF Core and from H2),docs/DIALECT.md(the SQL it generates),docs/ROADMAP.md(the plan),UPSTREAM.md(the EFCore.PG commit it forks from). - Still to do: the ADO layer is the
Npgsqlpackage for now; swapping it forH2Sharp.Datais a separate stage. - License: the fork keeps EFCore.PG's PostgreSQL License; the engine itself is MPL-2.0 / EPL-1.0 and is not part of that repository.