WitDatabase can be approached at four levels. Each one sits on top of the one below it and hides some of it, so the question is how much of the machinery you want in front of you.

The packages nest as well, which means the decision is cheap to revise. Installing the EF Core package brings all four levels with it, and dropping down to raw SQL later is an import statement rather than a dependency change.

The four levels

Level Package What you write What you give up
EF Core provider OutWit.Database.EntityFramework Classes, LINQ, migrations Some control over the SQL, and a little speed to the translation layer
ADO.NET OutWit.Database.AdoNet SQL, parameters, readers Change tracking, migrations, and the mapping you would otherwise write by hand
SQL engine OutWit.Database SQL, without connection and command objects The ADO.NET surface that other libraries expect to find
Core OutWit.Database.Core Keys and values, byte arrays SQL, schema, and every guarantee that depends on one

Most applications want the first row. The rest of this page is about the cases where they do not.

Replacing a database server in tests

Your application already runs against a database server through EF Core, whichever one that happens to be. You want the test suite to run without it, on a laptop, in CI, in a container that has nothing installed.

Use the EF Core provider, with UseWitDbInMemory() in the test fixture. The model, the migrations, the LINQ and the assertions all stay as they are, and only the Use… call changes.

Constraints, transactions and SQL parsing are real here, so a query that would fail against the server fails in the test too. EF Core's in-memory provider does none of that. And the parity being aimed at is with full database servers, which is why stored procedures and user-defined functions exist in an embedded engine at all.

Loading the database at runtime, from a plugin

This is the scenario WitDatabase was originally built for.

If your application loads assemblies at runtime, whether through AssemblyLoadContext, a plugin folder, an add-in host or a single-file publish, then a database with native components is a problem. The managed assembly resolves and the native library beside it does not, because native probing does not follow the same rules and frequently does not follow the plugin at all. It is not something you can configure your way out of reliably.

WitDatabase is one managed assembly. It loads the way every other .NET library in your plugin loads, because there is nothing else to find.

Use whichever level suits the plugin. If the plugin's job is to provide database support to a host that talks to EF Core, that is the EF Core provider, registered from inside the plugin with its own migrations. If the plugin needs a private store of its own, the Core API is smaller and has fewer moving parts.

A document format for a desktop application

You are writing something local and you need a place to keep the user's work. A .witdb file can be that place: one file, a real schema, indexes, transactions, and encryption under a password the user chooses. Changing that password later rewrites 60 bytes rather than the document, so it is an operation you can offer in a menu.

Use the EF Core provider if the document has a shape worth modelling, or ADO.NET if you would rather own the SQL. Read file format first: the format has changed between engine versions and the compatibility runs one way, so an application that writes a document with a newer engine and is then downgraded needs a plan.

Studio opens the file, shows the schema and the data, reports the format version and the encryption state, and runs queries against it. That is what you reach for when a support request arrives with a customer's file attached.

Running in the browser

Blazor WebAssembly works, with OutWit.Database.Core.IndexedDb installed alongside your chosen level. Storage goes to IndexedDB rather than to a file, and everything above the storage layer is unchanged.

Two constraints apply. The LSM store is unavailable in the browser, so use the B+Tree store, which is the default anyway. And AES has no hardware acceleration there, so add OutWit.Database.Core.BouncyCastle and use ChaCha20-Poly1305 if the data needs encrypting.

A cache, or state with no schema

If what you have is keys and values, and a schema would only be something to maintain, use Core directly. You get an ordered key-value store with transactions, encryption and a choice of storage engine, without the SQL layer on top of it. Keys sort, so a prefix scan gives you a range, which covers most of what a schema would have given you here.

What this choice does not settle

Picking a level does not pick a storage engine, an encryption algorithm, a journal or a cache policy. Those are set in the connection string or the builder and can be changed later without touching your code. See connection strings for the parameters and architecture for what each of them selects.

How many things can open the database

One process holds the file. Inside that process, as many connections as you like share a single engine and see each other's committed work, which covers a web application with a scoped DbContext per request. A second process is turned away.

This is how file databases generally behave, LiteDB included, and it is deliberate here. Letting several processes write to one file means a cross-process lock protocol, shared cache invalidation and a recovery story for a peer that died holding the lock, all for a shape that has a simpler answer: put the database behind the one process that owns it and give the others a service to talk to. The complexity would be paid on every read by every user to serve a case most applications do not have.

So the question to settle early is which process owns the file. If the answer is more than one, what you want is a database server, and WitDatabase will stand in for it during tests.

Where to go next