Repository navigation
Moving from LiteDB: a guide, an API mapping and an import tool #297
Description
From Discussion #14: @LeoYang06 suggested a comparison with LiteDB covering usage and design, and you replied that "we could provide a 'moving from LiteDb' section that could help developers in moving codebase, along with a migration tool from LiteDb". This issue scopes that for v6. It is part of #296 and fits the "desktop tools and internal apps on LiteDB" niche of #287.
Why now
LiteDB's last stable release is 5.0.21 (July 2024). 6.0 is in prerelease (6.0.0-prerelease.322 on NuGet, October 2026), and its release-readiness issue is still open. 6.x writes a new file format that 5.x cannot open; a writable open converts a 5.x file one way. Teams on LiteDB 5 are deciding now whether to move to 6.0 or elsewhere, and both paths start with "back up the file and migrate".
1. A guide: docs/moving-from-litedb.md
- What stays familiar: an embedded, single-file, BSON document database with LINQ, indexes and transactions.
- What is different, and why: async-only CRUD (since 4.0); source-generated mappers instead of
BsonMapperreflection (AOT and trimming);DocumentDbContextwithOnModelCreatinginstead ofGetCollection<T>;DynamicCollectionand BLQL (MQL-style JSON) instead of BsonExpression strings; field names stored lowercase; WAL sync modes (fix(wal): sync the WAL to disk before a commit is acknowledged #280) instead of LiteDB's commit model; multi-process WAL instead ofConnection=Shared. - An API mapping table, one row per LiteDB construct, each marked equivalent, different shape, different semantics or no equivalent. A first cut:
| LiteDB 5 | BLite | |
|---|---|---|
new LiteDatabase(path) |
a DocumentDbContext subclass (typed) or new BLiteEngine(path) (schema-less) |
different shape |
GetCollection<T>(name) |
a DocumentCollection<TId, T> property on the context |
equivalent |
GetCollection(name) (BsonDocument) |
engine.GetOrCreateCollection(name, …) (DynamicCollection) |
equivalent |
Insert, Update, Upsert, Delete, FindById |
InsertAsync, UpdateAsync, UpsertAsync, DeleteAsync, FindByIdAsync |
async only |
Find(pred), FindOne(pred) |
AsQueryable().Where(pred)…ToListAsync(), FirstOrDefaultAsync(pred) |
equivalent |
Query().Where().OrderBy().Limit() |
AsQueryable().Where().OrderBy().Take() |
equivalent |
"$.Name = 'x'" (BsonExpression) |
BLQL { "name": "x" } |
different syntax |
EnsureIndex(x => x.Name), unique |
HasIndex(x => x.Name, unique: true) in OnModelCreating |
equivalent |
Index on $.Address.City |
HasIndex(x => x.Address.City) |
equivalent |
[BsonId], [BsonField], [BsonIgnore] |
[Key], [Column], [NotMapped] |
equivalent |
BeginTrans / Commit / Rollback |
BeginTransaction(), CommitAsync() / RollbackAsync(), or SaveChangesAsync() |
different shape |
Rebuild() |
VacuumAsync(VacuumOptions) |
partial |
Password= (AES) |
CryptoOptions (AES-256-GCM) |
different format; cannot open a LiteDB file |
Connection=Shared |
AllowMultiProcessAccess (experimental, #288) |
different semantics |
[BsonRef], DbRef, Include |
store the id and query again | no equivalent |
FileStorage (GetStorage<T>) |
the KV store holds byte arrays; no file streams | no equivalent |
Multi-key index on an array, expression index, collation, Int32/Int64 auto-increment ids, custom serializers (RegisterType) |
to verify | |
| — | CDC (Watch), TimeSeries, vector search, geo, audit sink, GDPR primitives, hot backup |
BLite only |
- Honest limits: the rows marked no equivalent, and what to do instead.
- A comparison page (part of "Choosing BLite", Epic: v6.0.0 — migration guide, changelog, docs, samples and release engineering #296 section 3), pinned to LiteDB 5.0.21 and to 6.0 prerelease separately, dated, every fact read from the source at that version.
2. An import tool
A command (a dotnet tool, or a small BLite.Import.LiteDb package — your call) that copies a LiteDB file into a BLite database:
- Reads generically: every collection from
GetCollectionNames()asBsonDocument, the index definitions from$indexes, and reports what it cannot copy (FileStoragechunks,DbReflinks, expression indexes, collations) instead of dropping it silently. - Writes to
DynamicCollections and re-creates the indexes it can, with a summary of what was skipped. - Documents larger than one page.
DynamicCollectionrefuses a document overPageSize - 128bytes (about 16 KB with the default pages) and cannot read one a typed collection wrote (The schema-less API cannot read a typed collection that holds a document larger than one page: FindAllAsync and BLiteMigration throw "Skipping type 69" #301). LiteDB documents can be much larger, so the import needs The schema-less API cannot read a typed collection that holds a document larger than one page: FindAllAsync and BLiteMigration throw "Skipping type 69" #301 fixed, or writes those documents another way and reports them. - Converts types through LiteDB's typed reader, never by copying raw bytes:
Decimal,DateTime(LiteDB stores UTC and returns local time unless told otherwise; millisecond precision),Int32vsInt64kept as stored,ObjectId,Guid, binary,MinValue/MaxValue. - Never writes to the source. It opens the LiteDB file read-only, or works on a copy, because a writable open by LiteDB 6 converts a 5.x file one way. Encrypted sources take the password as input. A 4.x file needs LiteDB's own upgrade first; the tool says so instead of trying.
- Shipping: pins the LiteDB package it reads with (5.0.21 for v8/v9 files), and states which file versions it accepts.
3. A test that proves the import
- A differential oracle in the form of test: oracle tests for the LINQ provider and the typed collection store #250: generated documents of every type above are written with LiteDB, imported, and each one read back from BLite must equal the one LiteDB returns; counts and indexed queries (
==, ranges, ordering) must agree. A seed (for example, aDateTimewritten as local time) proves the test notices. - Fixtures: a few LiteDB files checked in as binary (see the
.gitattributesitem of Epic: v6.0.0 — migration guide, changelog, docs, samples and release engineering #296), including an encrypted one.
Questions
- A
dotnet tool, a package, or a command in Studio? - Should the import also write a typed context (generated entity classes), or stop at
DynamicCollections?
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
mrdevrobot commentedon Oct 18, 2026
Kept, with a reduced scope: a "Moving from LiteDB" section in the documentation with an API mapping. The import tool is out of scope for now. The v6 milestone was self-assigned and has been removed; the maintainers will schedule this.
LeoYang06 commentedon Oct 20, 2026
Two notes on the import tool mentioned at the end (for future prioritisation)
If this does get picked up again, my personal preference would be:
dotnet toolor a Studio feature could simply be a thin layer built on top of that package.DynamicCollection) is sufficient — generating entity class code isn't necessary. Developers migrating an existing project already have their C# entity classes; all they really need is to swap out the attributes (e.g.[BsonId]→[Key]) and declare aDocumentDbContext. Reverse-engineering C# classes from schemaless BSON is more likely to introduce errors around nullable fields or type inference. The one thing worth guaranteeing is that the data and field-name conventions written byDynamicCollectioncan later be read back seamlessly by the user's own strongly-typedDocumentDbContext.Thanks again to the team for all the work you do for the community!