AOT-compatible wrapper for LiteDB - Use LiteDB with Native AOT compilation and Clean Architecture
Community.LiteDB.Aot is a thin, AOT-compatible layer on top of LiteDB that enables:
- β‘ Native AOT compilation - Full support for .NET 8 Native AOT
- ποΈ Clean Architecture - Zero dependencies in domain entities
- π¨ EF Core-style API - Familiar
DbContextpattern - πΎ Same database format - Compatible with standard LiteDB files
- π Progressive migration - Use alongside existing LiteDB code
- π Source Generators - Compile-time mapper generation with Expression Trees
- π― DDD Support - Value Objects, Strongly-Typed IDs, Private Setters
- π·οΈ Data Annotations - [Key], [Required], [MaxLength], [Range] support
Full support for .NET 8 Native AOT with automatic trimming of reflection-based code (~30-40% size reduction).
Your domain entities stay pure - zero infrastructure dependencies:
// Clean domain entity
public class Order
{
public int OrderId { get; set; }
public decimal Total { get; set; }
public DateTime CreatedAt { get; set; }
}Compile-time mapper generation using Expression Trees for:
- β Private setters support (DDD Value Objects)
- β Complex nested objects (3+ levels deep)
- β Collections of nested objects (List)
- β Near-native performance (compiled delegates)
- β Full AOT compatibility (no runtime reflection)
Support for strongly-typed IDs to avoid primitive obsession:
public class OrderId
{
public Guid Value { get; private set; }
public OrderId(Guid value) => Value = value;
public static OrderId NewId() => new(Guid.NewGuid());
}
public class Order
{
public OrderId Id { get; private set; } = OrderId.NewId();
public string CustomerName { get; set; }
// ...
}Full support for standard Data Annotations attributes:
using System.ComponentModel.DataAnnotations;
public class UserProfile
{
[Key]
public int UserId { get; set; }
[Required]
[MaxLength(100)]
public string Username { get; set; }
[Range(18, 120)]
public int Age { get; set; }
}Supported attributes:
[Key]- Primary key detection (auto or manual)[Required]- Not null validation[MaxLength]- String length validation[Range]- Numeric range validation
Support for deeply nested objects and collections:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
// List of nested objects
public List<Address> Offices { get; set; } = new();
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public Location Coordinates { get; set; } // 2nd level nesting
}Full support for immutable Value Objects:
public class Money
{
public decimal Amount { get; private set; }
public string Currency { get; private set; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
}
public class Product
{
public int Id { get; set; }
public Money Price { get; private set; }
public void SetPrice(Money price) => Price = price;
}dotnet add package Community.LiteDB.Aot
dotnet add package Community.LiteDB.Aot.SourceGenerators// 1. Domain Entity (Clean - no dependencies)
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
// 2. DbContext (Infrastructure layer)
public partial class AppDbContext : LiteDbContext
{
public AotLiteCollection<Customer> Customers => Collection<Customer>();
public AppDbContext(string filename) : base(filename) { }
protected override void OnModelCreating(EntityModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.HasKey(x => x.Id).AutoIncrement();
entity.Property(x => x.Email).HasIndex().IsUnique();
entity.Property(x => x.Name).IsRequired().HasMaxLength(100);
});
}
}
// 3. Usage
using var db = new AppDbContext("myapp.db");
// Insert
var customer = new Customer { Name = "John", Email = "john@example.com", Age = 30 };
db.Customers.Insert(customer);
// Query
var adults = db.Customers.FindAll().Where(c => c.Age >= 18).ToList();
var john = db.Customers.FindById(new BsonValue(1));
// Update
customer.Age = 31;
db.Customers.Update(customer);
// Delete
db.Customers.Delete(new BsonValue(1));+-------------------------------------+
| Your Application (AOT-published) |
+-------------------------------------+
| Community.LiteDB.Aot (NEW) | <- Thin wrapper
| - LiteDbContext | <- Configuration API
| - AotLiteCollection<T> | <- Type-safe collections
+-------------------------------------+
| Source Generator (Compile-time) | <- Expression Trees
| - IEntityMapper<T> | <- BsonDocument <-> T
| - Expression Trees for setters | <- Private setters support
+-------------------------------------+
| LiteDB 5.0 (UNCHANGED) | <- Core engine
| + Engine.* (kept by trimmer) |
| + Document.* (kept by trimmer) |
| - Client.Mapper.* (TRIMMED) | <- Reflection removed!
+-------------------------------------+
Key benefit: The AOT trimmer automatically removes unused reflection-based parts of LiteDB (~30-40% size reduction)
using System.ComponentModel.DataAnnotations;
public class Product
{
[Key] // <- Source generator automatically detects this
public int ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// DbContext - NO configuration needed for [Key]!
public partial class AppDbContext : LiteDbContext
{
public AotLiteCollection<Product> Products => Collection<Product>();
protected override void OnModelCreating(EntityModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>(entity =>
{
entity.ToCollection("products");
});
}
}db.BeginTrans();
try
{
db.Customers.Insert(customer);
db.Orders.Insert(order);
db.Commit();
}
catch
{
db.Rollback();
throw;
}modelBuilder.Entity<Customer>(entity =>
{
entity.Property(x => x.Email)
.HasIndex("idx_email")
.IsUnique();
entity.Property(x => x.City)
.HasIndex("idx_city");
entity.Property(x => x.Name)
.IsRequired()
.HasMaxLength(100);
});You can use both packages in the same application:
// Old code (reflection-based)
var oldDb = new LiteDatabase("app.db");
var customersOld = oldDb.GetCollection<Customer>();
// New code (AOT-compatible)
var newDb = new AppDbContext("app.db");
var customersNew = newDb.Customers;
// Both access THE SAME database file!- Install
Community.LiteDB.Aot(keep LiteDB installed) - Create your
DbContextclass - Migrate repositories one at a time
- Remove old LiteDB code when done
Runtime package (~50 KB)
- Core interfaces and collections
LiteDbContextbase classAotLiteCollection<T>wrapper- Depends on
LiteDB >= 5.0.21
Compile-time source generator
- Automatic
IEntityMapper<T>generation - Expression Trees for property setters
- Support for private setters and nested objects
- [Key] attribute detection
- Data Annotations validation
The project includes comprehensive test suites:
- KeyAttributeTests - [Key] attribute detection and auto-increment
- PrivateSetterTests - DDD Value Objects with private setters
- ValueObjectIdTests - Strongly-typed IDs (OrderId, CustomerId, etc.)
- NestedObjectTests - Complex nested objects and collections
- DataAnnotationsTests - Validation attributes support
Run tests:
dotnet testCommunity.LiteDB.Aot is 2-4x faster than standard LiteDB!
| Scenario | Speedup | Details |
|---|---|---|
| Simple Entities | 2.3-3.5x | Object initializers |
| DDD Value Objects | 1.4-3.2x | Expression Trees for private setters |
| Complex Nested | 2.4-3.8x | Recursive mappers |
π Full benchmark results: BENCHMARK_RESULTS.md
Run benchmarks yourself:
cd Community.LiteDB.Aot.Benchmarks
dotnet run -c ReleaseExpected results:
- Expression Trees - Near-native performance for property access
- Compiled delegates - 100-1000x faster than reflection
- AOT trimming - 30-40% smaller executable size
Contributions are welcome! Please open issues or pull requests on GitHub.
This project is licensed under the MIT License.
- Repository: https://github.com/mrdevrobot/Community-LiteDb-AOT
- NuGet Package: https://www.nuget.org/packages/Community.LiteDB.Aot
- LiteDB: https://github.com/litedb-org/LiteDB
Built on top of the excellent LiteDB project by Mauricio David.
Made with β€οΈ by MrDevRobot