diff --git a/.github/workflows/build.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/build.yml rename to .github/workflows/ci.yml diff --git a/LICENSE b/LICENSE index 8b13789..c1fa817 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,22 @@ +MIT License + +Copyright (c)2026 Isaiah Clifford Opoku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 918df08..4e54daf 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ Every .NET project with EF Core ends up writing the same plumbing: soft delete f | **Entity Configuration Bases** | Fluent config base classes that auto-wire keys, indexes, and soft-delete defaults | | **Soft Delete** | Mark records as deleted with automatic global query filters; restore or hard-delete on demand | | **Audit Trail** | Auto-stamp `CreatedAt/By`, `UpdatedAt/By`; optional field-level `AuditLog` history | -| **Multi-Tenancy** | Automatic tenant filtering so each tenant only sees their own data | | **Repository + Unit of Work** | Generic `IRepository` / `IReadRepository` backed by `IUnitOfWork` | | **Specification Pattern** | Composable query specs with `And()` / `Or()` combinators, projection, and multi-column ordering | | **Pagination** | Offset (`ToPagedAsync`) and keyset/cursor (`ToKeysetPagedAsync`) pagination with `PagedResult` | @@ -250,7 +249,6 @@ await context.TruncateAsync(); | [Base Entities](docs/base-entities.md) | Entity hierarchy, configuration base classes | | [Soft Delete](docs/soft-delete.md) | ISoftDeletable, lifecycle methods, restoring records | | [Audit Trail](docs/audit-trail.md) | IAuditable, IFullAuditable, field-level AuditLog | -| [Multi-Tenancy](docs/multi-tenancy.md) | ITenantEntity, automatic filtering, tenant validation | | [Repository & Unit of Work](docs/repository-uow.md) | IRepository, IReadRepository, IUnitOfWork | | [Specification Pattern](docs/specifications.md) | Spec classes, And/Or combinators, projection specs | | [Pagination](docs/pagination.md) | Offset and keyset pagination, PagedResult | diff --git a/docs/audit-trail.md b/docs/audit-trail.md index 4eefa0f..e52e72d 100644 --- a/docs/audit-trail.md +++ b/docs/audit-trail.md @@ -168,3 +168,7 @@ The audit interceptor marks `CreatedAt` and `CreatedBy` as `IsModified = false` ## Combining with Soft Delete When an entity implements both `IAuditable` and `ISoftDeletable`, a soft delete triggers a `Modified` state change — so `UpdatedAt` and `UpdatedBy` are stamped at the moment of deletion. + +--- + +[← Soft Delete](soft-delete.md) | [Repository & Unit of Work →](repository-uow.md) diff --git a/docs/base-entities.md b/docs/base-entities.md index 500a516..7d8d5fc 100644 --- a/docs/base-entities.md +++ b/docs/base-entities.md @@ -181,3 +181,7 @@ catch (ConcurrencyConflictException ex) // ex.EntityId — primary key of the conflicting row } ``` + +--- + +[← Getting Started](getting-started.md) | [Soft Delete →](soft-delete.md) diff --git a/docs/dbcontext-utilities.md b/docs/dbcontext-utilities.md index 88bc018..259bb94 100644 --- a/docs/dbcontext-utilities.md +++ b/docs/dbcontext-utilities.md @@ -73,3 +73,7 @@ await context.TruncateAsync(); | `ExecuteInTransactionAsync(Func>)` | Same, returning a value | | `DetachAll()` | Clear the change tracker | | `TruncateAsync()` | Truncate a table by entity type | + +--- + +[← Query Helpers](query-helpers.md) | [Exceptions →](exceptions.md) diff --git a/docs/dynamic-filters.md b/docs/dynamic-filters.md index f7aae9c..cb6a1a4 100644 --- a/docs/dynamic-filters.md +++ b/docs/dynamic-filters.md @@ -158,3 +158,7 @@ var page = await context.Customers .ApplySorts(sorts) .ToPagedAsync(page: 1, pageSize: 20); ``` + +--- + +[← Pagination](pagination.md) | [Query Helpers →](query-helpers.md) diff --git a/docs/exceptions.md b/docs/exceptions.md index 2f26593..18794f1 100644 --- a/docs/exceptions.md +++ b/docs/exceptions.md @@ -187,3 +187,7 @@ catch (InvalidFilterException ex) return BadRequest(ex.Message); } ``` + +--- + +**Previous:** [← DbContext Utilities](dbcontext-utilities.md) diff --git a/docs/getting-started.md b/docs/getting-started.md index 889be52..c5cf103 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -169,3 +169,7 @@ Once configured, EfCoreKit handles the following via EF Core interceptors: - [Query Helpers](query-helpers.md) — WhereIf, OrderByDynamic, DbSet extensions - [DbContext Utilities](dbcontext-utilities.md) — Transactions, DetachAll, TruncateAsync - [Exceptions](exceptions.md) — All exception types, when they're thrown, what to catch + +--- + +**Next:** [Base Entities →](base-entities.md) diff --git a/docs/pagination.md b/docs/pagination.md index c7940e5..2218a64 100644 --- a/docs/pagination.md +++ b/docs/pagination.md @@ -135,3 +135,7 @@ public sealed class KeysetPagedResult | **Infinite scroll** | Possible | Ideal | | **Stable with concurrent inserts** | Can skip / duplicate rows | Always consistent | | **Requires ordered query** | Recommended | Required | + +--- + +[← Specification Pattern](specifications.md) | [Dynamic Filters →](dynamic-filters.md) diff --git a/docs/query-helpers.md b/docs/query-helpers.md index 49a3703..916fb9a 100644 --- a/docs/query-helpers.md +++ b/docs/query-helpers.md @@ -267,3 +267,7 @@ var spec = new SpecificationBuilder() var products = await context.Products.ApplySpecification(spec).ToListAsync(); ``` + +--- + +[← Dynamic Filters](dynamic-filters.md) | [DbContext Utilities →](dbcontext-utilities.md) diff --git a/docs/repository-uow.md b/docs/repository-uow.md index cbb7ac5..3365908 100644 --- a/docs/repository-uow.md +++ b/docs/repository-uow.md @@ -163,3 +163,7 @@ var orders = await repo.FindAsync(spec, ct); ``` See [Specification Pattern](specifications.md) for details. + +--- + +[← Audit Trail](audit-trail.md) | [Specification Pattern →](specifications.md) diff --git a/docs/soft-delete.md b/docs/soft-delete.md index 9a14440..4277942 100644 --- a/docs/soft-delete.md +++ b/docs/soft-delete.md @@ -165,3 +165,7 @@ When an entity implements both `IAuditable` and `ISoftDeletable` (as `SoftDeleta ## Combining with Multi-Tenancy Soft-deleted rows from other tenants remain invisible. The tenant filter and soft-delete filter are both applied independently. + +--- + +[← Base Entities](base-entities.md) | [Audit Trail →](audit-trail.md) diff --git a/docs/specifications.md b/docs/specifications.md index b82f41a..2b29be9 100644 --- a/docs/specifications.md +++ b/docs/specifications.md @@ -177,3 +177,7 @@ var page = await context.Orders.ToPagedFromSpecAsync( ``` `ToPagedFromSpecAsync` ignores any `Skip`/`Take` set on the spec — pagination is controlled by the `page`/`pageSize` parameters. + +--- + +[← Repository & Unit of Work](repository-uow.md) | [Pagination →](pagination.md)