A lightweight, Active Record-style ORM for .NET with first-class support for SQL Server, PostgreSQL, and MongoDB.
All connection types live in the ActiveForge namespace, so a single using ActiveForge; is sufficient regardless of the provider chosen.
ActiveForge streamlines data-centric development with a cohesive approach to entities, queries, and data management.
- Active Record pattern
Entities contain both state and persistence logic, removing the need for external repositories. - Type-safe fields
Provides wrappers for types (string, int, decimal, keys, etc.), tracking nullability and state, and handling conversion automatically. - Polymorphic mapping
Maps base types to concrete subtypes at runtime. - Custom field mappers
Easily support non-standard type conversions. - Field encryption
Simple attribute-based encryption/decryption for sensitive data.
- Composable predicates
Build queries with terms for equality, containment, ranges, null checks, and pattern matching; combine them with logical operators. - LINQ support
Write queries using familiar C# syntax, auto-translated to efficient ORM operations. - Pagination
Built-in paging with metadata for efficient handling of large datasets. - Lazy streaming
Stream results row-by-row for memory efficiency. - Field subsets
Load or update only the fields you need.
- Transactions
Explicit and nested transaction support; control scope via code or attributes. - Unit of Work
Integrated pattern for grouping multiple changes; supports both code-based and attribute-based usage. - Connection lifecycle
Connections and transactions are managed automatically on every write, ensuring reliability. - Batch operations
Queue up changes and execute them in bulk to reduce database round-trips.
- Auto-discovery & registration
Services marked with a simple interface are discovered and registered automatically. - Fluent builder API
Register all or selected services with fine-grained control. - Seamless DI integration
Simplifies service composition, testing, and enables proxy/interceptor scenarios.
| 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 β net472 excluded (native binaries risk) |
ActiveForge.MongoDB |
net8.0 Β· net9.0 Β· net10.0 Β· net472 Β· netstandard2.0 Β· netstandard2.1 |
using ActiveForge;
// SQL Server
var conn = new SqlServerConnection(
"Server=.;Database=Demo;Integrated Security=True;TrustServerCertificate=True;");
// PostgreSQL
var conn = new PostgreSQLConnection(
"Host=localhost;Database=demo;Username=app;Password=secret;");
// MongoDB
var conn = new MongoDataConnection(
"mongodb://localhost:27017",
"demo");
// SQLite
var conn = new SQLiteConnection("Data Source=app.db");
conn.Connect();Works in any DI host β ASP.NET Core, Worker Service, console, etc.
The provider-specific AddActiveForge* call registers the connection + UoW and returns an IActiveForgeBuilder.
Chain .AddServices() to auto-scan your assembly for IService implementations.
// Program.cs β choose one provider, then scan for IService implementations:
builder.Services
.AddActiveForgeSqlServer(
"Server=.;Database=Demo;Integrated Security=True;TrustServerCertificate=True;")
.AddServices(typeof(Program).Assembly);
builder.Services
.AddActiveForgePostgreSQL("Host=localhost;Database=demo;Username=app;Password=secret;")
.AddServices(typeof(Program).Assembly);
builder.Services
.AddActiveForgeMongoDB("mongodb://localhost:27017", "demo")
.AddServices(typeof(Program).Assembly);
builder.Services
.AddActiveForgeSQLite("Data Source=app.db")
.AddServices(typeof(Program).Assembly);Entity classes are provider-agnostic β the same class works with SQL Server, PostgreSQL, MongoDB, and SQLite.
using ActiveForge;
using ActiveForge.Attributes;
[Table("categories")]
public class Category : IdentityRecord
{
[Column("name")] public TString Name = new TString();
public Category() { }
public Category(DataConnection conn) : base(conn) { }
}
[Table("products")]
public class Product : IdentityRecord
{
[Column("name")] public TString Name = new TString();
[Column("price")] public TDecimal Price = new TDecimal();
[Column("in_stock")]
[DefaultValue(true)] public TBool InStock = new TBool();
[Column("created_at")]
[ReadOnly] public TDateTime CreatedAt = new TDateTime();
[Column("notes")]
[NoPreload] public TString Notes = new TString();
[Column("CategoryID")] public TForeignKey CategoryID = new TForeignKey();
// Embedded Record β triggers automatic INNER JOIN in queries
public Category Category;
public Product() { Category = new Category(); }
public Product(DataConnection conn) : base(conn) { Category = new Category(conn); }
}Naming conventions: PostgreSQL folds unquoted identifiers to lower-case β use lower-case
[Table]and[Column]values. MongoDB uses the attribute values as BSON field and collection names verbatim.Key attributes:
[ReadOnly]β included in SELECT but never written.[NoPreload]β excluded from the default SELECT; include viaFieldSubset.[DefaultValue]β pre-populates the field on construction.[Sensitive]β masks values in diagnostic output.[Encrypted]β transparent encrypt/decrypt at ORM layer.
using ActiveForge.Query;
using ActiveForge.Linq;
// ββ INSERT ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
var p = new Product(conn);
p.Name.SetValue("Widget");
p.Price.SetValue(9.99m);
p.InStock.SetValue(true);
p.Insert(); // p.ID is populated automatically after insert
// ββ READ by primary key βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
var p2 = new Product(conn);
p2.ID.SetValue(1);
p2.Read(); // throws PersistenceException if not found
// ββ QUERY (QueryTerm API) βββββββββββββββββββββββββββββββββββββββββββββββββββββ
var template = new Product(conn);
var inStock = new EqualTerm(template, template.InStock, true);
var byName = new OrderAscending(template, template.Name);
var results = conn.QueryAll(template, inStock, byName, 0, null);
// ββ QUERY (LINQ) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
List<Product> page = conn.Query(new Product(conn))
.Where(x => x.InStock == true && x.Price < 50m)
.OrderBy(x => x.Name)
.Skip(0).Take(20)
.ToList();
// ββ QUERY with JOIN filter ββββββββββββββββββββββββββββββββββββββββββββββββββββ
List<Product> electronics = conn.Query(new Product(conn))
.Where(x => x.Category.Name == "Electronics")
.OrderBy(x => x.Price)
.ToList();
// ββ UPDATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
p.Price.SetValue(14.99m);
p.Update(RecordLock.UpdateOption.IgnoreLock); // update all columns
p.Notes.SetValue("On sale");
p.UpdateChanged(); // update only changed columns
// ββ DELETE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
p.Delete(); // delete by PK
// Delete by predicate:
var disc = new EqualTerm(template, template.InStock, false);
template.Delete(disc);Implement IService on your class alongside a service interface. Castle DynamicProxy handles
open β begin β commit β close with no virtual methods or framework coupling required.
using ActiveForge;
using ActiveForge.Transactions;
// ββ Interface (consumed by controllers / other services)
public interface IOrderService
{
Order GetById(int id);
void Ship(int orderId);
}
// ββ Implementation β IService triggers auto-registration
public class OrderService : IOrderService, IService
{
private readonly DataConnection _conn;
public OrderService(DataConnection conn) { _conn = conn; }
public Order GetById(int id) { ... }
[Transaction] // opens connection, begins tx, commits/rolls back, closes connection
public void Ship(int orderId)
{
var order = new Order(_conn);
order.ID.SetValue(orderId);
_conn.Read(order);
order.Status.SetValue("Shipped");
order.Update(RecordLock.UpdateOption.IgnoreLock);
// commit on success; rollback + connection close on exception
}
}
// Register β auto-scan picks up OrderService, registers as IOrderService:
builder.Services
.AddActiveForgeSqlServer("Server=...;...")
.AddServices(typeof(Program).Assembly);
// Inject by interface β proxy is transparent:
public class CheckoutController : ControllerBase
{
public CheckoutController(IOrderService orders) { _orders = orders; }
[HttpPost("{id}/ship")]
public IActionResult Ship(int id) { _orders.Ship(id); return NoContent(); }
}Manual usage (standalone, no DI):
var conn = new SqlServerConnection("...");
var uow = new SqlServerUnitOfWork(conn);
var svc = ActiveForgeServiceFactory.Create(new OrderService(conn), conn, uow);
svc.Ship(42);
// Or With.Transaction for ad-hoc work:
With.Transaction(uow, () =>
{
order.Status.SetValue("Shipped");
order.Update(RecordLock.UpdateOption.IgnoreLock);
shipment.Insert();
});| Guide | Description |
|---|---|
| Getting Started | Step-by-step tutorial |
| Field Types | All TField types and their operators |
| Query Builder | Composing WHERE, ORDER BY, and pagination |
| Transactions & DI | Manual transactions, IUnitOfWork, With.Transaction, [Transaction] interceptor, DI service proxies |
| LINQ Querying | conn.Query<T>() LINQ support |
| Field Subsets | Partial fetches and partial updates |
| Advanced | Encryption, custom mappers, polymorphism |
| Wiki | Comprehensive reference β all concepts with examples |
ActiveForge/
βββ src/
β βββ ActiveForge/ β Core library (provider-agnostic)
β β βββ Attributes/ β [Table], [Column], [Identity], etc.
β β βββ Adapters/ β Abstract adapter interfaces
β β βββ Fields/ β TString, TInt, TDecimal, ... (25+ types)
β β βββ Linq/ β LINQ query support
β β βββ Query/ β QueryTerm, SortOrder, EqualTerm, ...
β β βββ Transactions/ β IUnitOfWork, BaseUnitOfWork, With,
β β TransactionInterceptor, ActiveForgeServiceFactory
β βββ ActiveForge.SqlServer/ β SQL Server provider
β β βββ Adapters/ β SqlAdapterCommand/Connection/Reader/Transaction
β β βββ Extensions/ β AddActiveForgeSqlServer()
β β βββ Transactions/ β SqlServerUnitOfWork
β β βββ SqlServerConnection.cs
β βββ ActiveForge.PostgreSQL/ β PostgreSQL provider
β β βββ Adapters/ β NpgsqlAdapterCommand/Connection/Reader/Transaction
β β βββ Extensions/ β AddActiveForgePostgreSQL()
β β βββ Transactions/ β PostgreSQLUnitOfWork
β β βββ PostgreSQLConnection.cs
β βββ ActiveForge.MongoDB/ β MongoDB provider
β β βββ Extensions/ β AddActiveForgeMongoDB()
β β βββ Internal/ β MongoMapper, MongoQueryTranslator, MongoTypeCache
β β βββ Transactions/ β MongoUnitOfWork
β β βββ MongoDataConnection.cs
β βββ ActiveForge.SQLite/ β SQLite provider
β βββ Adapters/ β SQLiteAdapterCommand/Connection/Reader/Transaction
β βββ Extensions/ β AddActiveForgeSQLite()
β βββ Transactions/ β SQLiteUnitOfWork
β βββ SQLiteConnection.cs
βββ tests/
β βββ ActiveForge.Tests/ β Core library tests (340 tests)
β βββ ActiveForge.SqlServer.Tests/ β SQL Server provider tests (80 tests)
β βββ ActiveForge.PostgreSQL.Tests/ β PostgreSQL provider tests (81 tests)
β βββ ActiveForge.MongoDB.Tests/ β MongoDB provider tests (126 tests)
β βββ ActiveForge.SQLite.Tests/ β SQLite provider tests (52 tests)
βββ examples/
β βββ ActiveForge.Examples/ β Runnable console examples
βββ docs/ β Documentation
We welcome contributions! Please see our Contributing Guide for details.
- π Bug Reports - Create an issue
- π‘ Feature Requests - Start a discussion
- π Documentation - Help improve our docs
- π» Code - Submit pull requests
Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)
