Getting Started with LiteDB — Installation, Basics, and ExamplesLiteDB is a lightweight, fast, embedded NoSQL database for .NET applications. It stores documents in BSON (Binary JSON) format, supports ACID transactions, and runs in-process without a separate server. This makes it ideal for desktop apps, small services, mobile apps using Xamarin/.NET MAUI, and any scenario that needs a simple, zero-configuration, file-based data store.
Why choose LiteDB?
- Embedded and serverless: the database runs inside your application process and stores data in a single .db file.
- No external dependencies: add the NuGet package and you’re ready.
- Document-oriented: store flexible, schema-less documents (POCOs) with fast BSON serialization.
- ACID transactions: safe writes with journal/locking support.
- Indexes and LINQ support: create indexes on fields and query using LINQ or LiteDB’s query API.
- Small footprint: minimal memory and disk overhead.
Installation
You can install LiteDB via NuGet. For .NET CLI:
dotnet add package LiteDB
Or via Package Manager Console:
Install-Package LiteDB
For .NET 6/7/8/9 projects the same package works; ensure your project targets a supported runtime. If you use LiteDB v5 or v6 features, check the package version and migration notes.
Basic concepts
- Database: represented by a single file (e.g., data.db).
- Collection: like a table — a set of documents of the same kind.
- Document: a BSON object; you can map C# POCOs directly.
- BsonDocument: LiteDB’s raw document representation (similar to a dictionary).
- Id: each document has an _id field (default type is ObjectId or BsonValue types).
- Index: supports single-field and compound indexes for faster queries.
Opening a database and CRUD with POCOs
Here’s a concise example using a simple POCO class:
using LiteDB; using System; using System.Collections.Generic; public class Person { public int Id { get; set; } // LiteDB will treat 'Id' as the document id public string Name { get; set; } public int Age { get; set; } public List<string> Tags { get; set; } } class Program { static void Main() { // Open (or create) a database file using var db = new LiteDatabase(@"MyData.db"); // Get (or create) a collection named "people" var col = db.GetCollection<Person>("people"); // Create indexes col.EnsureIndex(x => x.Name); col.EnsureIndex(x => x.Age); // Insert a document var person = new Person { Name = "Alice", Age = 30, Tags = new List<string>{"admin","team"} }; col.Insert(person); // Id is set automatically if int and 0 // Read documents var results = col.Find(x => x.Age > 20); foreach (var p in results) { Console.WriteLine($"{p.Id}: {p.Name} ({p.Age})"); } // Update person.Age = 31; col.Update(person); // Delete col.Delete(person.Id); } }
Notes:
- LiteDB maps property named “Id” or “_id” as the document identifier by convention. You can use other types like ObjectId or Guid.
- For thread safety, use a single LiteDatabase instance per file and share it across threads. LiteDB manages concurrency internally.
Working with BsonDocument (dynamic use)
If you prefer a schema-less approach or want to manipulate documents dynamically:
using(var db = new LiteDatabase("MyData.db")) { var col = db.GetCollection("products"); var doc = new BsonDocument { ["name"] = "Widget", ["price"] = 9.99, ["stock"] = 100 }; col.Insert(doc); var res = col.FindOne(Query.EQ("name", "Widget")); Console.WriteLine(res["price"]); }
BsonDocument gives full control over document structure and types.
Queries and indexes
LiteDB supports LINQ and its own query API. Use indexes to speed up queries:
var col = db.GetCollection<Person>("people"); // LINQ var adults = col.Find(p => p.Age >= 18 && p.Name.StartsWith("A")); // Query API var q = Query.And(Query.GTE("Age", 18), Query.StartsWith("Name", "A")); var adults2 = col.Find(q);
To create compound indexes, use string paths or expressions:
col.EnsureIndex("Tags[]"); // index array items col.EnsureIndex(x => x.Name); // expression-based
File storage (File Storage API)
LiteDB has a file storage system (like GridFS) to store binary files inside the same .db file:
using var db = new LiteDatabase("MyData.db"); var fs = db.FileStorage; // Upload a file using var fsIn = File.OpenRead("photo.jpg"); fs.Upload("photo.jpg", fsIn); // Download using var fsOut = File.Create("downloaded.jpg"); fs.Download("photo.jpg", fsOut); // List files foreach (var file in fs.Find(Query.All())) { Console.WriteLine($"{file.Id} - {file.Filename}"); }
Files are stored in a special collection and can have metadata.
Transactions and concurrency
LiteDB supports ACID transactions. When using the default settings, write operations are transactional. For multi-threaded apps, reuse the same LiteDatabase instance. If you need multiple processes accessing the same file, LiteDB supports shared access with a lock file, but performance characteristics differ—embedded databases are primarily designed for single-process access.
Backup and Compact
- Backup: copy the .db file while the database is closed, or use the built-in Backup API to export safely.
- Compact: to reduce file size after many deletions, use the Shrink/Checkpoint/Export methods depending on LiteDB version. Example:
db.Rebuild(); // compacts and rebuilds file (method name varies by version)
Check the version docs for exact API names (Rebuild, Shrink, or Compact).
Migrations and schema versioning
Since LiteDB is schema-less, adding new fields to POCOs is straightforward. For structural changes (renaming fields, transforming data), write a small migration routine:
var col = db.GetCollection<Person>("people"); foreach(var doc in col.FindAll()) { // e.g., rename field "FullName" to "Name" if(doc["FullName"] != null) { doc["Name"] = doc["FullName"]; doc.Remove("FullName"); col.Update(doc); } }
Run migrations once at startup when upgrading application versions.
Examples & patterns
- Offline-first apps: store data locally in LiteDB and sync with a server when online. Keep a queue collection for pending changes, track timestamps and GUIDs for conflict resolution.
- Settings store: use a small collection for app settings (one document per namespace).
- Caching layer: cache API responses in LiteDB to survive restarts.
- Local logs: append-only collection for local audit logs (compact periodically).
Example: simple sync queue pattern
public class SyncItem { public int Id { get; set; } public string Entity { get; set; } public string Operation { get; set; } // Create/Update/Delete public BsonDocument Data { get; set; } public DateTime QueuedAt { get; set; } }
Push SyncItem entries to the queue collection and process them on connectivity.
Tips & best practices
- Reuse a single LiteDatabase instance per file to maximize performance and thread-safety.
- Create indexes on fields you query frequently.
- Prefer typed POCO collections when possible for clarity and compile-time safety.
- Use BsonDocument for flexible, dynamic data but be mindful of type handling.
- Periodically compact the database if you perform many deletions or large file operations.
- Keep backups before running destructive migrations.
Troubleshooting common issues
- Locked file errors: ensure only one process writes or use proper shutdown. Check for lingering handles in other processes.
- Unexpected nulls/missing fields: schema-less nature means older documents may lack new fields—use safe access patterns.
- Large file growth: perform compaction/Rebuild after many deletes or large file uploads.
Resources
- Official LiteDB documentation and GitHub repository for API references, version-specific migration guides, and examples.
LiteDB is a practical choice when you need an easy-to-embed, fast, document database for .NET with minimal configuration. The examples above should get you started with installation, basic CRUD, file storage, and common patterns like offline sync.
Leave a Reply