Skip to content

Feature: Entity Framework Core provider (BLite.EntityFrameworkCore) #9

@mrdevrobot

Description

@mrdevrobot

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 → QueryDescriptor pipeline (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-down
  • DocumentDbContext with 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

  1. Create src/BLite.EntityFrameworkCore/BLite.EntityFrameworkCore.csproj (net10.0, ref EF Core 10.x)
  2. Implement UseBLite(connectionString) → parses host/port/apiKey/tls, creates BLiteClient
  3. Implement UseBLiteEmbedded(connectionString) → parses data source path, creates BLiteEngine directly
  4. BLiteOptionsExtension : IDbContextOptionsExtension — registers all provider services
  5. Register core replacements: IDatabase, IDbContextTransactionManager, IQueryCompilationContextFactory, ITypeMappingSource, IModelValidator, IDatabaseCreator, IValueGeneratorSelector

Phase 2: Model & conventions

  1. BLiteConventionSetBuilder: entity → collection, owned → embedded BSON sub-document, reject many-to-many and navigation properties
  2. BLiteTypeMappingSource: string/int/long/double/decimal/bool/DateTime/Guid/byte[] → BsonType
  3. BLiteModelValidator: error on composite keys, TPH/TPT inheritance, table splitting, navigation properties to non-owned entities

Phase 3: Database CRUD (IDatabase)

  1. BLiteDatabase.SaveChangesAsync(IList<IUpdateEntry>): group entries by entity type, Added→Insert, Modified→Update, Deleted→Delete, bulk batch where possible
  2. Internal abstraction: IBLiteDataStore with two implementations — RemoteDataStore (BLite.Client/gRPC) and EmbeddedDataStore (BLite.Core)
  3. BLiteTransactionManager: wrap transaction in IDbContextTransaction (gRPC: RemoteTransaction; embedded: BLiteSession)
  4. BLiteDatabaseCreator: EnsureCreatedAsync creates collections + indexes, EnsureDeletedAsync drops

Phase 4: Query pipeline

  1. BLiteQueryableMethodTranslatingVisitor: translate ShapedQueryExpressionQueryDescriptor (reuse existing FilterOp mappings)
  2. BLiteShapedQueryCompilingVisitor: compile into executable delegate, IAsyncEnumerable<T> via query
  3. Special methods: Any() → Take(1), Contains()FilterOp.In, Sum/Average → server push-down

Phase 5: Value generation & materialization

  1. BLiteValueGeneratorSelector: ObjectId/Guid auto-generation for Added entities
  2. EfCoreDocumentMapper<TId, T>: bridge between IEntityType metadata and BsonDocument

Phase 6: Testing & samples

  1. Unit tests: type mapping, convention validation, query translation, SaveChanges grouping
  2. Integration tests using embedded mode (no external server needed — runs in-process)
  3. Integration tests using gRPC mode (requires running BLite.Server)
  4. 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

  1. Optimistic concurrency: BLite has no built-in _etag or 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.
  2. Enum mapping: Default to string storage or integer? (Recommend string for readability, configurable)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions