Releases: CodeShayk/ActiveForge
v1.0.0
ActiveForge ORM — v1.0.0
We are excited to announce the first release of ActiveForge — a lightweight, Active Record-style ORM for .NET ported and modernised from a proven .NET 3.5 foundation. ActiveForge brings type-safe persistence, composable querying, LINQ support, nested transactions, and DI-friendly service proxying to .NET 8 through .NET 10, with provider support for SQL Server, PostgreSQL, MongoDB, and SQLite.
Packages
dotnet add package ActiveForge.Core
dotnet add package ActiveForge.SqlServer # SQL Server
dotnet add package ActiveForge.PostgreSQL # PostgreSQL
dotnet add package ActiveForge.MongoDB # MongoDB
dotnet add package ActiveForge.SQLite # SQLite / in-memory testingWhat's included
Entities & Mapping
Entities follow the Active Record pattern — state and persistence logic live together in the same class, eliminating the need for a separate repository layer. Entities are defined once and work across all providers without modification.
Type-safe fields replace plain CLR properties with wrapper types (TString, TInt, TDecimal, TBool, TDateTime, TForeignKey, and 20+ more). Each field tracks its own nullability and dirty state, handles value conversion automatically, and participates in change detection for partial updates.
Attributes control the mapping between entity fields and the underlying store:
| Attribute | Effect |
|---|---|
[Table] |
Maps the class to a named table / collection |
[Column] |
Maps a field to a named column / BSON field |
[Identity] |
Marks the primary key; auto-populated after insert |
[ReadOnly] |
Included in SELECT but never written |
[NoPreload] |
Excluded from the default SELECT; loaded on demand via FieldSubset |
[DefaultValue] |
Pre-populates the field on construction |
[Encrypted] |
Transparent encrypt / decrypt at the ORM layer |
[Sensitive] |
Masks the value in diagnostic output |
Additional mapping capabilities: polymorphic mapping (base type resolved to concrete subtype at runtime), custom field mappers for non-standard type conversions, and field subsets for loading or updating only a named subset of columns.
Querying
QueryTerm API — build queries by composing predicate objects: EqualTerm, ContainsTerm, LikeTerm, NullTerm, RangeTerm, GreaterOrEqualTerm, LessOrEqualTerm, and their logical combinations. Sort with OrderAscending / OrderDescending including multi-column CombinedSortOrder.
LINQ support — conn.Query<T>() returns an IQueryable<T> that translates Where, OrderBy, ThenBy, Take, and Skip into native ORM operations. Cross-join predicates and sort are fully supported — filter or order by a field on an embedded (joined) entity directly in the lambda:
conn.Query(new Product(conn))
.Where(p => p.Category.Name == "Electronics" && p.Price < 500m)
.OrderBy(p => p.Category.Name)
.ThenBy(p => p.Price)
.Skip(0).Take(20)
.ToList();Join type overrides — class-level join types (INNER JOIN / LEFT OUTER JOIN) can be overridden per query without changing the entity definition:
conn.Query(new Product(conn))
.LeftOuterJoin<Category>()
.Where(p => p.Category.Name == "Electronics")
.ToList();Pagination — QueryPage returns results with total-count metadata. Lazy streaming — LazyQueryAll yields rows one at a time for memory-efficient processing of large result sets.
Transactions & Unit of Work
ActiveForge provides three complementary ways to manage transactions:
With.Transaction — wrap an ad-hoc block of work:
With.Transaction(uow, () =>
{
order.Status.SetValue("Shipped");
order.Update(RecordLock.UpdateOption.IgnoreLock);
shipment.Insert();
});IUnitOfWork — fine-grained programmatic control with CreateTransaction, Commit, and Rollback. Nesting is depth-tracked; only the outermost scope commits to the database.
[Transaction] attribute — declarative demarcation via Castle DynamicProxy. Applying the attribute to a service method causes the proxy to open the connection, begin the transaction, commit on success, and roll back and close the connection on exception — with no virtual methods or base class coupling required.
Dependency Injection & Service Proxy
ActiveForge integrates with any .NET DI host (ASP.NET Core, Worker Service, console) through provider-specific IServiceCollection extension methods.
One-call registration:
builder.Services
.AddActiveForgeSqlServer("Server=.;Database=Demo;Integrated Security=True;")
.AddServices(typeof(Program).Assembly); // auto-scan for IService implementationsIService marker — implement IService on any class to opt it into auto-discovery. AddServices() scans the supplied assemblies, registers each implementation against its non-system interfaces, and wraps it in a Castle interface proxy that activates [Transaction] and [ConnectionScope] interceptors transparently.
ActiveForgeServiceFactory — available for standalone (non-DI) scenarios where a proxy is needed without a container.
Provider highlights
SQL Server
- Full ADO.NET adapter layer over
Microsoft.Data.SqlClient5.2.1 SELECT @@IDENTITYfor identity retrieval (compatible withsp_executesqlexecution scope)- Pessimistic locking support
PostgreSQL
- Full Npgsql 8.0.3 adapter layer
RETURNINGclause for identity retrieval after insert- Pessimistic locking with
FOR UPDATEsemantics
MongoDB
- Reflection-based BSON mapping — no serializer attributes or configuration required
QueryTermpredicates translate to MongoDBFilterDefinition($eq,$in,$gte,$lte, regex forLikeTerm)- Automatic
$lookup+$unwindaggregation pipeline for embedded Record fields (joins) - Auto-increment integer IDs via a
__activeforge_counterscollection;[Identity]maps to_id - Multi-document transaction support via
MongoUnitOfWork
SQLite
- Full
Microsoft.Data.Sqlite8.0.0 adapter layer - In-memory database support (
Data Source=:memory:) — ideal for fast integration tests without a server
Target Frameworks
| Package | Frameworks |
|---|---|
ActiveForge.Core |
net8.0 · net9.0 · net10.0 · net472 · netstandard2.0 · netstandard2.1 |
ActiveForge.SqlServer |
net8.0 · net9.0 · net10.0 · net472 · netstandard2.0 · netstandard2.1 |
ActiveForge.PostgreSQL |
net8.0 · net9.0 · net10.0 (limited by Npgsql 8) |
ActiveForge.SQLite |
net8.0 · net9.0 · net10.0 · netstandard2.0 · netstandard2.1 |
ActiveForge.MongoDB |
net8.0 · net9.0 · net10.0 · net472 · netstandard2.0 · netstandard2.1 |
Test coverage
679 tests — 0 failures across all five providers.
| Suite | Tests |
|---|---|
| ActiveForge.Tests (Core) | 340 |
| ActiveForge.SqlServer.Tests | 80 |
| ActiveForge.PostgreSQL.Tests | 81 |
| ActiveForge.MongoDB.Tests | 126 |
| ActiveForge.SQLite.Tests | 52 |
Each provider suite covers full CRUD, join queries, pagination, transactions, and Unit of Work lifecycle.
Documentation
| Guide | |
|---|---|
| Getting Started | Install, connect, define entities, run your first query |
| Field Types | All TField types, operators, and conversion behaviour |
| Query Builder | Composing WHERE, ORDER BY, pagination, and joins |
| Transactions & DI | IUnitOfWork, With.Transaction, [Transaction], service proxies |
| LINQ Querying | conn.Query<T>(), cross-join predicates, join overrides |
| Field Subsets | Partial fetches and partial updates |
| Advanced | Encryption, custom mappers, polymorphism |
| Wiki | Comprehensive reference with examples |
Full commit history: https://github.com/CodeShayk/ActiveForge/commits/v1.0.0