-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
Summary
Build BLite.EntityFrameworkCore — an EF Core non-relational provider that sits on top of BLite.Client (gRPC) or BLite.Core (embedded), mapping DbContext/DbSet<T>/LINQ/SaveChanges to BLite's typed collection CRUD and QueryDescriptor.
Feasible with significant effort: the query pipeline is ~80% ready, but schema management requires custom implementation.
Feasibility Analysis
What BLite already provides ✅
- Full LINQ →
QueryDescriptorpipeline (Where, OrderBy, Select, Skip, Take, Count, Sum, Average) - Typed collections with
IDocumentMapper<TId, T>(BSON ↔ C# bidirectional) - Async CRUD:
InsertAsync,UpdateAsync,DeleteAsync,FindByIdAsync,InsertBulkAsync - Transactions:
BeginTransactionAsync→ Commit/Rollback - Secondary indexes with LINQ expression key selectors
- Source Generators for mapper code generation
IBLiteQueryable<T>with server-side push-downDocumentDbContextwith Unit-of-Work pattern (embedded mode)
Gaps the provider must bridge
| Gap | Severity | Mitigation |
|---|---|---|
| No relationships / foreign keys | By design | BLite is a document database — no navigations, no Include(). Owned entities only (embedded BSON sub-documents). |
| No GROUP BY server push-down | MEDIUM | Client-side evaluation (like MongoDB/Cosmos EF providers) |
| No schema migrations | MEDIUM | Only EnsureCreated (create collections + indexes); no Migrations runner (BLite is schema-less) |
| No auto-increment IDs | LOW | Default to ObjectId/Guid; optional int via engine sequence |
| No composite keys | LOW | Reject at model validation with clear error |
| No optimistic concurrency | LOW | Tracked in dedicated issue on BLite engine repo — requires engine-level auto-increment field or _version convention |
Change tracking: EF Core's built-in snapshot change tracker works out of the box — no custom implementation needed.
Proposed Architecture
User Code: DbContext + DbSet<T> + LINQ + SaveChanges
│
▼
BLite.EntityFrameworkCore (new project)
├─ UseBLite(connectionString) — gRPC mode
├─ UseBLiteEmbedded(path) — embedded mode (local dev / testing)
├─ BLiteOptionsExtension (DI wiring)
├─ BLiteDatabase : IDatabase (SaveChanges → Insert/Update/Delete)
├─ BLiteTransactionManager (wraps transaction)
├─ BLiteQueryCompilationContext (LINQ → QueryDescriptor)
├─ BLiteConventionSetBuilder (entity→collection, owned→embedded)
├─ BLiteTypeMappingSource (CLR → BsonType)
├─ BLiteValueGeneratorSelector (ObjectId/Guid auto-gen)
└─ BLiteDatabaseCreator (EnsureCreated/Deleted)
│
├─► BLite.Client (gRPC) → BLite.Server [production]
└─► BLite.Core (embedded BLiteEngine) [dev / test]
Connection String Format
UseBLite("Host=localhost;Port=2626;ApiKey=my-key;UseTls=true")
UseBLiteEmbedded("Data Source=./data/mydb.db;PageSize=16384")
Implementation Plan
Phase 1: Project scaffold & DI plumbing
- Create
src/BLite.EntityFrameworkCore/BLite.EntityFrameworkCore.csproj(net10.0, ref EF Core 10.x) - Implement
UseBLite(connectionString)→ parses host/port/apiKey/tls, createsBLiteClient - Implement
UseBLiteEmbedded(connectionString)→ parses data source path, createsBLiteEnginedirectly BLiteOptionsExtension : IDbContextOptionsExtension— registers all provider services- Register core replacements:
IDatabase,IDbContextTransactionManager,IQueryCompilationContextFactory,ITypeMappingSource,IModelValidator,IDatabaseCreator,IValueGeneratorSelector
Phase 2: Model & conventions
BLiteConventionSetBuilder: entity → collection, owned → embedded BSON sub-document, reject many-to-many and navigation propertiesBLiteTypeMappingSource: string/int/long/double/decimal/bool/DateTime/Guid/byte[] → BsonTypeBLiteModelValidator: error on composite keys, TPH/TPT inheritance, table splitting, navigation properties to non-owned entities
Phase 3: Database CRUD (IDatabase)
BLiteDatabase.SaveChangesAsync(IList<IUpdateEntry>): group entries by entity type, Added→Insert, Modified→Update, Deleted→Delete, bulk batch where possible- Internal abstraction:
IBLiteDataStorewith two implementations —RemoteDataStore(BLite.Client/gRPC) andEmbeddedDataStore(BLite.Core) BLiteTransactionManager: wrap transaction inIDbContextTransaction(gRPC:RemoteTransaction; embedded:BLiteSession)BLiteDatabaseCreator:EnsureCreatedAsynccreates collections + indexes,EnsureDeletedAsyncdrops
Phase 4: Query pipeline
BLiteQueryableMethodTranslatingVisitor: translateShapedQueryExpression→QueryDescriptor(reuse existing FilterOp mappings)BLiteShapedQueryCompilingVisitor: compile into executable delegate,IAsyncEnumerable<T>via query- Special methods:
Any()→ Take(1),Contains()→FilterOp.In,Sum/Average→ server push-down
Phase 5: Value generation & materialization
BLiteValueGeneratorSelector: ObjectId/Guid auto-generation for Added entitiesEfCoreDocumentMapper<TId, T>: bridge betweenIEntityTypemetadata andBsonDocument
Phase 6: Testing & samples
- Unit tests: type mapping, convention validation, query translation, SaveChanges grouping
- Integration tests using embedded mode (no external server needed — runs in-process)
- Integration tests using gRPC mode (requires running BLite.Server)
- Sample
BLite.EfCore.Sample: Blog/Post model with owned Address type
Key Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Provider type | Non-relational (like MongoDB EF / Cosmos EF) | BLite is a document database; no SQL generation, views, stored procedures |
| Relationships | None — owned entities only (embedded BSON) | BLite has no foreign keys, no JOINs. Navigations to non-owned entities are rejected at model validation. |
| Migrations | None — EnsureCreated/EnsureDeleted only |
BLite is schema-less |
| Change tracking | EF Core built-in snapshot tracker | Works out of the box; no proxy or lazy-loading needed |
| Dual target | gRPC (BLite.Client) and embedded (BLite.Core) from day one | Embedded mode is essential for local dev and testing without running BLite.Server |
| Connection | ConnectionString-based API |
Standard pattern in EF Core ecosystem; familiar to developers |
| Reference impl | Microsoft.EntityFrameworkCore.Cosmos |
Closest architectural match — same document-database constraints |
Open Questions
- Optimistic concurrency: BLite has no built-in
_etagor auto-increment fields. See dedicated issue on the BLite engine repo for engine-level support. Until then, concurrency tokens are not supported by the provider. - Enum mapping: Default to string storage or integer? (Recommend string for readability, configurable)
Reactions are currently unavailable