Azka.BaseProject
10.0.0-rc.1
dotnet add package Azka.BaseProject --version 10.0.0-rc.1
NuGet\Install-Package Azka.BaseProject -Version 10.0.0-rc.1
<PackageReference Include="Azka.BaseProject" Version="10.0.0-rc.1" />
<PackageVersion Include="Azka.BaseProject" Version="10.0.0-rc.1" />
<PackageReference Include="Azka.BaseProject" />
paket add Azka.BaseProject --version 10.0.0-rc.1
#r "nuget: Azka.BaseProject, 10.0.0-rc.1"
#:package Azka.BaseProject@10.0.0-rc.1
#addin nuget:?package=Azka.BaseProject&version=10.0.0-rc.1&prerelease
#tool nuget:?package=Azka.BaseProject&version=10.0.0-rc.1&prerelease
Azka.BaseProject
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 -
CoreEntitywith internal ID, public ID, document lifecycle fields, and audit metadata - Multi-Tenant Entity Base -
CoreEntityMultiTenantwith 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
DeletedAtis set - Audit Metadata Handling - Add, update, and delete operations resolve the current user through
IUserSsoService - SSO User Contract -
UserSsoandIUserSsoServiceabstractions for authenticated user data and access checks - Document Lifecycle Enum -
DocumentTypefor 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 publicGuid.AddAsync(...)fillsCreatedAtandCreatedBy.UpdateAsync(...)fillsUpdatedAtandUpdatedBy.DeleteAsync(...)performs a soft delete by fillingDeletedAtandDeletedBy.
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
Links
| Product | Versions 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. |
-
net10.0
- Azka (>= 10.0.0-rc.1)
- Azka.PostgreSQL (>= 10.0.0-rc.1)
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