Four layers, each on top of the one below it, each hiding some of it. The packages nest too, so installing the top one brings all four and moving down later is an import statement.
| Layer | Package | You write |
|---|---|---|
| EF Core | OutWit.Database.EntityFramework |
Classes, LINQ, migrations |
| ADO.NET | OutWit.Database.AdoNet |
SQL, parameters, readers |
| SQL engine | OutWit.Database |
SQL, without connection objects |
| Core | OutWit.Database.Core |
Keys and values |
Most applications want the first row. Choosing an API walks through the cases where they do not, by scenario rather than by layer.
Connection strings applies to all of them and is the only place where the parameters are listed.
What each one is for
EF Core is the reason most projects reach for this database: your model, your migrations, your LINQ, and one line naming the provider. Bulk operations are there for the writes that should not go through change tracking.
ADO.NET is where you write the SQL yourself, with the surface any .NET developer already knows.
Take it when a library expects a DbConnection, or when a query is easier to write than to express
in LINQ.
The SQL engine is the same SQL without the connection and command objects, plus two things the ADO.NET surface has no place for: schema introspection typed to this engine, and direct access to the iterators the planner would have used.
Core is the ordered key-value store the rest is built on. No schema, no SQL, and everything else intact: transactions, encryption, a choice of storage engine.
Async
Every layer is async throughout rather than async on top of something synchronous. EF Core's
…Async methods, ADO.NET's OpenAsync, ExecuteReaderAsync and ReadAsync, the engine's own
calls and the core store's GetAsync, PutAsync, DeleteAsync and ScanAsync all go down to
storage without a synchronous call in the middle.
That matters in one place in particular: the browser has no synchronous I/O, so the async path is the only path there. See platform support.
They are the same database
All four open the same file and see the same data. A test fixture can create a schema through EF Core migrations and then check a row through ADO.NET; an application can use EF Core throughout and drop to the engine for the one report that LINQ makes awkward.
The layer decides how much machinery stands between you and the database, and nothing else.
Studio opens the same file too, which is worth knowing while you are still writing the code: a schema you generated and a row you wrote can be looked at rather than queried for.