Azka 10.0.0-alpha3

This is a prerelease version of Azka.
dotnet add package Azka --version 10.0.0-alpha3
                    
NuGet\Install-Package Azka -Version 10.0.0-alpha3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Azka" Version="10.0.0-alpha3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Azka" Version="10.0.0-alpha3" />
                    
Directory.Packages.props
<PackageReference Include="Azka" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Azka --version 10.0.0-alpha3
                    
#r "nuget: Azka, 10.0.0-alpha3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Azka@10.0.0-alpha3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Azka&version=10.0.0-alpha3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Azka&version=10.0.0-alpha3&prerelease
                    
Install as a Cake Tool

Azka Framework

NuGet .NET

A lightweight, high-performance data access framework for .NET that provides Repository, Unit of Work, and Specification patterns with fluent query building capabilities.

Features

  • Repository Pattern - Clean abstraction for CRUD operations
  • Unit of Work Pattern - Transaction management with nested transaction support
  • Specification Pattern - Composable queries with fluent API
  • High-Performance Mapping - Fast object mapper using DynamicMethod compilation
  • Relationship Support - One-to-many and many-to-one relationships with automatic JOIN handling
  • Metadata-Driven - Automatic metadata generation through reflection
  • Attribute-Based Configuration - Simple attributes for entity-to-table mapping

Installation

dotnet add package Azka

Quick Start

1. Define Your Entity

using Azka.Common.Annotations;

[Table("Users")]
public class User
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Column("username")]
    public required string Username { get; set; }

    [Column("email")]
    public required string Email { get; set; }

    // One-to-many relationship
    [WithMany("user_id")]
    public ICollection<Post> Posts { get; set; } = new List<Post>();
}

[Table("Posts")]
public class Post
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Column("title")]
    public required string Title { get; set; }

    [Column("user_id")]
    public int UserId { get; set; }

    // Many-to-one relationship
    [WithOne("user_id")]
    public User? User { get; set; }
}

2. Create a Specification

using Azka.Common.Abstractions.Specifications;

public class ActiveUserPostsSpecification : Specification<User>
{
    public ActiveUserPostsSpecification(int userId)
    {
        Query.Where(u => u.Id == userId);
        Query.Include(u => u.Posts);
        Query.OrderByDesc(u => u.Id);
    }
}

3. Use Repository via Unit of Work

public interface IAppUnitOfWork : IUnitOfWork
{
    IRepository<User> Users { get; }
}

// Inject only Unit of Work
public class UserService
{
    private readonly IAppUnitOfWork _unitOfWork;

    public UserService(IAppUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<User?> GetUserWithPostsAsync(int userId)
    {
        var spec = new ActiveUserPostsSpecification(userId);
        return await _unitOfWork.Users.SingleOrDefaultAsync(spec);
    }

    public async Task AddUserAsync(User user)
    {
        await _unitOfWork.Users.AddAsync(user);
    }
}

4. Use Unit of Work

public interface IAppUnitOfWork : IUnitOfWork
{
    IRepository<User> Users { get; }
    IRepository<Post> Posts { get; }
}

public class UserService
{
    private readonly IAppUnitOfWork _unitOfWork;

    public UserService(IAppUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task CreateUserWithPostAsync(User user, Post post)
    {
        var token = _unitOfWork.Begin();
        try
        {
            user = await _unitOfWork.Users.AddAsync(user);
            post.UserId = user.Id;
            await _unitOfWork.Posts.AddAsync(post);

            _unitOfWork.Commit(token);
        }
        catch
        {
            _unitOfWork.Rollback();
            throw;
        }
    }
}

Specification Builder API

The Specification<T> base class provides a fluent API for building queries:

Method Description
Where(Expression) Add a filter condition
Include(Expression) Include related entities
OrderBy(Expression) Sort ascending
OrderByDesc(Expression) Sort descending
Skip(int) Skip N records
Take(int) Take N records
public class ComplexQuerySpecification : Specification<User>
{
    public ComplexQuerySpecification(string email, int page, int pageSize)
    {
        Query.Where(u => u.Email.Contains(email));
        Query.Include(u => u.Posts);
        Query.OrderBy(u => u.Username);
        Query.Skip((page - 1) * pageSize);
        Query.Take(pageSize);
    }
}

Attributes

Attribute Target Description
[Table] Class Maps entity to table name and schema
[Column] Property Maps property to column name
[Key] Property Identifies primary key
[WithOne] Property Configures many-to-one relationship (takes foreign key column name)
[WithMany] Property Configures one-to-many relationship (takes foreign key column name)

Note: Properties marked with [Key] must also include [Column("...")] so the primary key column name can be resolved.

Attribute Examples

[Table("Users", "dbo")]          // With custom schema
public class User { }

[Column("first_name")]           // Map to different column name
public string FirstName { get; set; }

[WithOne("department_id")]        // Foreign key column name
public Department? Department { get; set; }

[WithMany("department_id")]       // Foreign key column name
public ICollection<Employee> Employees { get; set; }

Mapping Rules (Entity Mapper)

  • Entities must have a public parameterless constructor.
  • [Key] must be paired with [Column("...")] to resolve the primary key column.
  • Columns are read from SELECT aliases in the format alias_column (case-insensitive); this is handled automatically when using the built-in query helpers.
  • If a property has a backing field named _{camelCaseProperty}, the mapper will set the backing field directly.

Backing Field Mapping Example

If a property has a private backing field named _{camelCaseProperty}, the mapper sets the backing field directly.

[Table("users")]
public class User
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    private string _username = string.Empty;

    [Column("username")]
    public string Username
    {
        get => _username;
        set => _username = value;
    }

    [Column("email")]
    public string Email { get; set; } = string.Empty;
}

Backing Field for One-to-Many (Read-Only List Exposure)

Use a private List<T> backing field for [WithMany], and expose it as a read-only collection to avoid sharing a mutable list reference.

[Table("users")]
public class User
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Column("username")]
    public string Username { get; set; } = string.Empty;

    [Column("email")]
    public string Email { get; set; } = string.Empty;

    private List<Post> _posts = new();

    [WithMany("user_id")]
    public IReadOnlyCollection<Post> Posts => _posts.AsReadOnly();

    public void AddPost(Post post)
    {
        if (post == null) throw new ArgumentNullException(nameof(post));
        if (_posts.Contains(post)) return;
        post.UserId = Id;
        post.User = this;
        _posts.Add(post);
    }

    public void RemovePost(Post post)
    {
        if (post == null) throw new ArgumentNullException(nameof(post));
        if (_posts.Remove(post))
        {
            post.UserId = default;
            post.User = null;
        }
    }
}

[Table("posts")]
public class Post
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Column("title")]
    public string Title { get; set; } = string.Empty;

    [Column("user_id")]
    public int UserId { get; set; }

    [WithOne("user_id")]
    public User? User { get; set; }
}

Unit of Work API

The Unit of Work pattern manages transactions with support for nested transactions:

public interface IUnitOfWork : IDisposable
{
    TransactionToken Begin();              // Returns a token for tracking
    void Commit(TransactionToken token);    // Commit with token
    void Rollback();                       // Rollback all
}

Note: Create a custom UnitOfWork implementation (e.g., IAppUnitOfWork) that inherits from BaseUnitOfWork to expose repositories, so services only inject the Unit of Work:

public sealed class AppUnitOfWork : BaseUnitOfWork, IAppUnitOfWork
{
    public AppUnitOfWork(IServiceProvider serviceProvider, IDbConnection connection)
        : base(serviceProvider, connection)
    {
    }

    public IRepository<User> Users => GetRepository<IRepository<User>>();
    public IRepository<Post> Posts => GetRepository<IRepository<Post>>();
}

Nested Transaction Support

public async Task ComplexOperationAsync()
{
    var token = _unitOfWork.Begin();
    try
    {
        // This can call other methods that also call Begin()
        await NestedOperationAsync();

        _unitOfWork.Commit(token);
    }
    catch
    {
        _unitOfWork.Rollback();
        throw;
    }
}

private async Task NestedOperationAsync()
{
    var nestedToken = _unitOfWork.Begin();
    try
    {
        // Do work here
        _unitOfWork.Commit(nestedToken);  // Only commits if parent commits
    }
    catch
    {
        _unitOfWork.Rollback();
        throw;
    }
}

Requirements

License

See LICENSE.txt for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Azka:

Package Downloads
Azka.PostgreSQL

PostgreSQL database provider for Azka Framework, providing high-performance data access using Npgsql.

Azka.CQRS

A lightweight CQRS library for .NET that provides command/query pipelines, validation, hooks, and DI integration.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0-alpha3 93 2/1/2026
10.0.0-alpha2 87 1/31/2026
10.0.0-alpha1 82 1/19/2026
4.0.0-alpha.48 166 4/30/2025
4.0.0-alpha.47 124 4/25/2025
4.0.0-alpha.46 113 4/25/2025
4.0.0-alpha.45 129 4/25/2025
4.0.0-alpha.44 169 4/24/2025
4.0.0-alpha.43 200 4/15/2025
4.0.0-alpha.42 190 4/14/2025
4.0.0-alpha.41 192 4/14/2025
4.0.0-alpha.40 175 4/10/2025
4.0.0-alpha.39 169 4/10/2025
4.0.0-alpha.38 172 4/10/2025
4.0.0-alpha.37 175 4/9/2025
4.0.0-alpha.36 168 4/8/2025
4.0.0-alpha.35 164 4/8/2025
4.0.0-alpha.34 115 12/7/2024
4.0.0-alpha.33 115 12/7/2024
4.0.0-alpha.32 104 12/7/2024
4.0.0-alpha.31 99 12/6/2024
4.0.0-alpha.30 104 12/6/2024
4.0.0-alpha.29 105 12/6/2024
4.0.0-alpha.28 101 12/2/2024
4.0.0-alpha.27 106 12/2/2024
4.0.0-alpha.26 106 12/2/2024
4.0.0-alpha.25 108 12/1/2024
4.0.0-alpha.24 108 12/1/2024
4.0.0-alpha.23 101 12/1/2024
4.0.0-alpha.22 108 12/1/2024
4.0.0-alpha.21 101 12/1/2024
4.0.0-alpha.20 112 12/1/2024
4.0.0-alpha.19 106 11/30/2024
4.0.0-alpha.18 112 11/24/2024
4.0.0-alpha.17 114 11/12/2024
4.0.0-alpha.16 125 5/6/2024
4.0.0-alpha.15 142 2/8/2024
4.0.0-alpha.14 167 1/1/2024
4.0.0-alpha.13 176 12/16/2023
4.0.0-alpha.12 132 12/16/2023
4.0.0-alpha.11 135 12/16/2023
4.0.0-alpha.10 137 12/15/2023
4.0.0-alpha.9 169 11/12/2023
4.0.0-alpha.8 127 11/10/2023
4.0.0-alpha.7 209 4/28/2023
4.0.0-alpha.6 209 4/28/2023
4.0.0-alpha.5 204 4/26/2023
4.0.0-alpha.4 194 4/24/2023
4.0.0-alpha.3 200 4/24/2023
4.0.0-alpha.2 211 4/20/2023
4.0.0-alpha.1 216 4/20/2023
3.0.0-alpha.6 348 12/29/2022
3.0.0-alpha.5 232 12/5/2022
3.0.0-alpha.4 247 10/15/2022
3.0.0-alpha.2 239 10/15/2022
3.0.0-alpha.1 246 10/15/2022
2.0.1-alpha1 331 6/8/2022
2.0.1-alpha.6 253 10/14/2022
2.0.1-alpha.5 246 10/2/2022
2.0.1-alpha.4 256 9/19/2022
2.0.1-alpha.3 242 9/10/2022
2.0.1-alpha.2 279 7/14/2022
2.0.1-alpha.1 282 6/11/2022
2.0.0 747 5/21/2022
0.0.1-alpha.4 274 5/9/2022
0.0.1-alpha.3 270 2/6/2022
0.0.1-alpha.2 305 2/5/2022
0.0.1-alpha.1 297 1/22/2022

Initial release of Azka Framework
     - Repository pattern with CRUD operations
     - Unit of Work pattern with nested transaction support
     - Specification pattern for composable queries
     - High-performance object mapper
     - Support for one-to-many and many-to-one relationships