Azka.BaseProject 10.0.0-rc.1

This is a prerelease version of Azka.BaseProject.
dotnet add package Azka.BaseProject --version 10.0.0-rc.1
                    
NuGet\Install-Package Azka.BaseProject -Version 10.0.0-rc.1
                    
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.BaseProject" Version="10.0.0-rc.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Azka.BaseProject" Version="10.0.0-rc.1" />
                    
Directory.Packages.props
<PackageReference Include="Azka.BaseProject" />
                    
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.BaseProject --version 10.0.0-rc.1
                    
#r "nuget: Azka.BaseProject, 10.0.0-rc.1"
                    
#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.BaseProject@10.0.0-rc.1
                    
#: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.BaseProject&version=10.0.0-rc.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Azka.BaseProject&version=10.0.0-rc.1&prerelease
                    
Install as a Cake Tool

Azka.BaseProject

NuGet .NET

Reusable base project primitives for Azka applications. This package provides auditable document entities, multi-tenant document fields, a core repository abstraction, soft-delete behavior, public ID lookup, and SSO user contracts for filling audit metadata.

Azka.BaseProject is intended to sit above the Azka core framework and database provider packages. It gives application projects a consistent base model and repository shape, while the application still owns concrete entities, database registration, and SSO user resolution.

What This Package Provides

  • Core Document Entity - CoreEntity with internal ID, public ID, document lifecycle fields, and audit metadata
  • Multi-Tenant Entity Base - CoreEntityMultiTenant with company and company branch scoping fields
  • Core Repository Contract - ICoreRepository<T> extends Azka repository contracts with public ID lookup
  • Core Repository Base - CoreRepository<T> for common active-document queries, add, update, and soft delete
  • Soft Delete Filtering - Read operations automatically exclude rows where DeletedAt is set
  • Audit Metadata Handling - Add, update, and delete operations resolve the current user through IUserSsoService
  • SSO User Contract - UserSso and IUserSsoService abstractions for authenticated user data and access checks
  • Document Lifecycle Enum - DocumentType for draft, original, and archived document states

What This Package Does Not Provide

  • A concrete SSO implementation
  • Application-specific permission rules
  • Database table migrations or schema scripts
  • Automatic dependency injection registration
  • A complete application Unit of Work

Applications are expected to implement IUserSsoService, create concrete entities and repositories, and register the database provider such as Azka.PostgreSQL.

Installation

dotnet add package Azka.BaseProject

This package references the Azka core framework and Azka PostgreSQL provider from the solution package graph.

Core Concepts

Core Entity

Inherit from CoreEntity when an entity needs the standard document fields used by Azka application projects.

using Azka.BaseProject;
using Azka.Common.Annotations;

[Table("public", "products")]
public class Product : CoreEntity
{
    [Column("name")]
    public string Name { get; set; } = string.Empty;

    [Column("description")]
    public string? Description { get; set; }
}

CoreEntity includes:

Property Type Column Default Description
Id long id 0 Internal generated primary key. This property uses [Key] and [GeneratedNumber], so it should normally be generated by the database provider instead of being filled manually.
PublicId Guid public_id Guid.NewGuid() Public identifier that can be exposed to clients without exposing the internal database ID.
OriginalId long? original_id null Reference to the original document in edit or copy flows. First-time create data usually leaves this field empty.
SourceId long? source_id null Reference to the copied draft source when an update is performed through a draft flow.
IsSaved bool is_saved false Indicates whether the document has already been saved by the user.
DocumentType DocumentType document_type DocumentType.DRAFT Current lifecycle state of the document.
CreatedAt DateTime created_at DateTime.UtcNow UTC timestamp when the document was created. CoreRepository<T>.AddAsync(...) refreshes this value before insert.
CreatedBy string created_by null! User name of the authenticated user who created the document. Filled by CoreRepository<T>.AddAsync(...).
UpdatedAt DateTime? updated_at null UTC timestamp when the document was last updated. Filled by CoreRepository<T>.UpdateAsync(...).
UpdatedBy string? updated_by null User name of the authenticated user who last updated the document. Filled by CoreRepository<T>.UpdateAsync(...).
DeletedAt DateTime? deleted_at null UTC timestamp when the document was soft-deleted. Filled by CoreRepository<T>.DeleteAsync(...).
DeletedBy string? deleted_by null User name of the authenticated user who soft-deleted the document. Filled by CoreRepository<T>.DeleteAsync(...).

Multi-Tenant Entity

Use CoreEntityMultiTenant when the document should be scoped by company and optionally by company branch.

using Azka.BaseProject;
using Azka.Common.Annotations;

[Table("public", "purchase_orders")]
public class PurchaseOrder : CoreEntityMultiTenant
{
    [Column("document_number")]
    public string DocumentNumber { get; set; } = string.Empty;

    [Column("total_amount")]
    public decimal TotalAmount { get; set; }
}

CoreEntityMultiTenant adds:

Property Column Description
CompanyCode company_code Company that owns or scopes the document
CompanyBranchCode company_branch_code Company branch that owns or scopes the document

Both properties are nullable strings. The base repository does not automatically apply company or branch filters; application repositories or services should add those filters through specifications according to the current user's access rules.

Document Lifecycle

DocumentType represents the lifecycle state of a document.

Value Numeric Value Description
DRAFT 0 Unsaved create or edit state
ORIGINAL 1 Main saved document used as the current source of truth
ARCHIVED -1 Historical backup document state

Repository

Create application repositories by inheriting from CoreRepository<T>.

using Azka.BaseProject;
using Azka.Common.Database;

public interface IProductRepository : ICoreRepository<Product>
{
}

public sealed class ProductRepository : CoreRepository<Product>, IProductRepository
{
    public ProductRepository(
        IDatabaseContext databaseContext,
        IUserSsoService userSsoService)
        : base(databaseContext, userSsoService)
    {
    }
}

CoreRepository<T> delegates persistence and querying to IDatabaseContext, then applies application-level behavior:

  • Read operations wrap incoming specifications with DeletedAt == null.
  • GetByIdAsync(...) returns an active document by internal primary key.
  • GetByPublicIdAsync(...) returns an active document by public Guid.
  • AddAsync(...) fills CreatedAt and CreatedBy.
  • UpdateAsync(...) fills UpdatedAt and UpdatedBy.
  • DeleteAsync(...) performs a soft delete by filling DeletedAt and DeletedBy.

Repository API Reference

ICoreRepository<T> extends Azka.Common.Abstractions.Repositories.IViewRepository<T> and adds core document write methods, internal ID lookup, and public ID lookup.

Constructor:

Signature Parameter Description
CoreRepository(IDatabaseContext databaseContext, IUserSsoService userSsoService) databaseContext Database context used to execute query, insert, and update operations. Passing null throws ArgumentNullException.
CoreRepository(IDatabaseContext databaseContext, IUserSsoService userSsoService) userSsoService Current-user service used to fill CreatedBy, UpdatedBy, and DeletedBy.

Query methods:

Method Parameters Returns Behavior
Task<IEnumerable<T>> ListAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: query filter, include, ordering, skip, and take rules. cancellationToken: cancellation token. Active documents that match the specification. Combines the provided specification with DeletedAt == null.
Task<T?> FirstOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: query rules. cancellationToken: cancellation token. First active matching document, or null. Uses active-document filtering before querying.
Task<T> FirstAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: query rules. cancellationToken: cancellation token. First active matching document. Uses active-document filtering before querying. The underlying database context controls the exception behavior when no row is found.
Task<T?> SingleOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: query rules. cancellationToken: cancellation token. Single active matching document, or null. Uses active-document filtering before querying. The underlying database context controls the exception behavior when more than one row is found.
Task<T> SingleAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: query rules. cancellationToken: cancellation token. Single active matching document. Uses active-document filtering before querying. The underlying database context controls the exception behavior when zero or multiple rows are found.
Task<long> CountAsync(ISpecification<T> specification, CancellationToken cancellationToken = default) specification: count filter rules. cancellationToken: cancellation token. Number of active matching documents. Uses active-document filtering before counting.
Task<T> GetByIdAsync(long id, CancellationToken cancellationToken = default) id: internal primary key value. cancellationToken: cancellation token. Active document with the matching Id. Filters by Id and excludes soft-deleted rows.
Task<T> GetByPublicIdAsync(Guid publicId, CancellationToken cancellationToken = default) publicId: public document identifier. cancellationToken: cancellation token. Active document with the matching PublicId. Filters by PublicId and excludes soft-deleted rows.

Write methods:

Method Parameters Returns Behavior
Task<T> AddAsync(T model, CancellationToken cancellationToken = default) model: document to insert. cancellationToken: cancellation token. Added document returned by the database context. Resolves the current user, sets CreatedAt = DateTime.UtcNow, and sets CreatedBy to user.UserName or "Anonymous" when the user name is null.
Task<int> UpdateAsync(T model, CancellationToken cancellationToken = default) model: document with updated values. cancellationToken: cancellation token. Number of affected records. Returns 0 when model.Id == 0 or no active row exists for the ID. Otherwise sets UpdatedAt = DateTime.UtcNow, sets UpdatedBy, and updates the row.
Task<int> DeleteAsync(long id, CancellationToken cancellationToken = default) id: internal primary key value of the document to delete. cancellationToken: cancellation token. Number of affected records. Loads the active document. Returns 0 when not found. Otherwise sets DeletedAt = DateTime.UtcNow, sets DeletedBy, and updates the row instead of physically deleting it.

The repository uses internal specifications for common filters:

Specification Purpose
ActiveCoreEntitySpecification<T> Copies the incoming specification's filters, includes, ordering, skip, and take values, then adds DeletedAt == null.
IdSpecification<T> Filters by Id and DeletedAt == null.
PublicIdSpecification<T> Filters by PublicId and DeletedAt == null.

Repository Usage

using Azka.Common.Abstractions.Specifications;

public sealed class ProductByNameSpecification : Specification<Product>
{
    public ProductByNameSpecification(string name)
    {
        Query.Where(product => product.Name == name);
    }
}

public sealed class ProductService
{
    private readonly IProductRepository _products;

    public ProductService(IProductRepository products)
    {
        _products = products;
    }

    public Task<Product?> FindByNameAsync(string name, CancellationToken cancellationToken = default)
    {
        return _products.FirstOrDefaultAsync(
            new ProductByNameSpecification(name),
            cancellationToken);
    }

    public Task<Product> FindByPublicIdAsync(Guid publicId, CancellationToken cancellationToken = default)
    {
        return _products.GetByPublicIdAsync(publicId, cancellationToken);
    }
}

SSO User Service

Applications must implement IUserSsoService so repositories can resolve audit metadata and services can check access permissions.

using Azka.BaseProject;

public sealed class AppUserSso : UserSso
{
}

public sealed class UserSsoService : IUserSsoService
{
    public Task<UserSso> GetCurrentUserAsync(CancellationToken cancellationToken = default)
    {
        UserSso user = new AppUserSso
        {
            UserName = "admin",
            Email = "admin@example.com",
            FullName = "Application Admin",
            CompanyCode = "AZKA",
            CompanyBranchCode = "HQ"
        };

        return Task.FromResult(user);
    }

    public Task<bool> IsAllowAccessAsync(string permission, CancellationToken cancellationToken = default)
    {
        return Task.FromResult(true);
    }

    public Task<bool> IsAllowAccessAllCompany()
    {
        return Task.FromResult(false);
    }

    public Task<bool> IsAllowAccessAllCompanyBranch()
    {
        return Task.FromResult(false);
    }
}

Register the implementation in dependency injection with your repositories and database provider.

builder.Services.AddScoped<IUserSsoService, UserSsoService>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();

SSO API Reference

IUserSsoService is the application-owned contract used by this package to resolve the authenticated user and permission checks.

Method Parameters Returns Used By
Task<UserSso> GetCurrentUserAsync(CancellationToken cancellationToken = default) cancellationToken: cancellation token. Current authenticated user data. CoreRepository<T>.AddAsync(...), UpdateAsync(...), and DeleteAsync(...) use this method to fill audit metadata.
Task<bool> IsAllowAccessAsync(string permission, CancellationToken cancellationToken = default) permission: permission code or name to check. cancellationToken: cancellation token. true when access is allowed; otherwise false. Application services can use this for feature or action permission checks.
Task<bool> IsAllowAccessAllCompany() None. true when the user can access documents from all companies; otherwise false. Application services or repositories can use this before applying company filters.
Task<bool> IsAllowAccessAllCompanyBranch() None. true when the user can access documents from all company branches; otherwise false. Application services or repositories can use this before applying branch filters.

UserSso contains the authenticated user's identity and scope information:

Property Type Description
UserName string? User name used for audit fields.
Email string? User email address.
FullName string? User display name.
CompanyCode string? Company scope for the current user.
CompanyBranchCode string? Company branch scope for the current user.

Database Mapping

Because CoreEntity already maps standard fields with Azka attributes, database tables should include the matching columns.

CREATE TABLE public.products
(
    id bigint PRIMARY KEY,
    public_id uuid NOT NULL,
    original_id bigint NULL,
    source_id bigint NULL,
    is_saved boolean NOT NULL,
    document_type integer NOT NULL,
    created_at timestamp with time zone NOT NULL,
    created_by text NOT NULL,
    updated_at timestamp with time zone NULL,
    updated_by text NULL,
    deleted_at timestamp with time zone NULL,
    deleted_by text NULL,
    name text NOT NULL,
    description text NULL
);

CREATE SEQUENCE products_id_seq START 1;

CoreEntity.Id uses [GeneratedNumber], so the PostgreSQL provider expects the standard generated-number sequence convention:

{table_name}_{column_name}_seq

For the products.id example, the expected sequence is products_id_seq.

Soft Delete Behavior

DeleteAsync(...) does not physically remove the row. It loads the active document, fills deletion metadata, and updates the record.

var affectedRows = await _products.DeleteAsync(productId, cancellationToken);

After deletion, repository read operations exclude the record because CoreRepository<T> applies the active-document specification.

Requirements

  • .NET 10.0 or higher
  • Azka core framework
  • Azka PostgreSQL provider
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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0-rc.1 43 6/30/2026
10.0.0-alpha6 48 6/20/2026
10.0.0-alpha.9 49 6/25/2026
10.0.0-alpha.8 45 6/25/2026
10.0.0-alpha.7 49 6/25/2026

Initial release of Azka.BaseProject
     - Core auditable document entity
     - Multi-tenant entity base
     - Core repository abstraction and base implementation
     - Soft-delete and active-document specifications
     - Public ID lookup and SSO user contracts