Skip to content

CodeShayk/ActiveForge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

41 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

logo ActiveForge ORM v1.0.0

License: MIT GitHub Release master-build master-codeql

A lightweight, Active Record-style ORM for .NET with first-class support for SQL Server, PostgreSQL, and MongoDB.


Packages

Package Version Description
ActiveForge.Core ActiveForge.Core Core β€” entities, fields, predicates, LINQ, transactions, adapters, Castle proxy factory for automatic UoW handling.
ActiveForge.SqlServer ActiveForge.SqlServer SQL Server provider β€” SqlServerConnection, ADO.NET adapters, SqlServerUnitOfWork, DI extensions
ActiveForge.PostgreSQL ActiveForge.PostgreSQL PostgreSQL provider β€” PostgreSQLConnection, Npgsql adapters, PostgreSQLUnitOfWork, DI extensions
ActiveForge.MongoDB ActiveForge.MongoDB MongoDB provider β€” MongoDataConnection, BSON mapping, MongoUnitOfWork, DI extensions
ActiveForge.SQLite ActiveForge.SQLite SQLite provider β€” SQLiteConnection, Microsoft.Data.Sqlite adapters, SQLiteUnitOfWork, DI extensions

All connection types live in the ActiveForge namespace, so a single using ActiveForge; is sufficient regardless of the provider chosen.


Features

ActiveForge streamlines data-centric development with a cohesive approach to entities, queries, and data management.

πŸ—‚ Entities & Mapping

  • 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.

πŸ” Querying

  • 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.

πŸ’Ύ Data Management

  • 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.

🌐 Dependency Injection & Service Proxy

  • 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.

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 β€” net472 excluded (native binaries risk)
ActiveForge.MongoDB net8.0 Β· net9.0 Β· net10.0 Β· net472 Β· netstandard2.0 Β· netstandard2.1

Quick Start

1. Connect (standalone)

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();

2. Register with DI

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);

3. Define entities

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 via FieldSubset. [DefaultValue] β€” pre-populates the field on construction. [Sensitive] β€” masks values in diagnostic output. [Encrypted] β€” transparent encrypt/decrypt at ORM layer.

4. CRUD

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);

5. Service proxy β€” automatic connection & transaction management

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();
});

Documentation

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

Project Layout

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

Contributing

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

Credits

Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)