Skip to content

Commit b005b3b

Browse files
committed
feat: add GitHub Packages publishing and update project documentation
1 parent 78dad7f commit b005b3b

4 files changed

Lines changed: 184 additions & 9 deletions

File tree

.github/workflows/publish.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ jobs:
5656
--api-key ${{ secrets.NUGET_API_KEY }}
5757
--skip-duplicate
5858
59+
- name: Publish to GitHub Packages
60+
run: >
61+
dotnet nuget push ./artifacts/*.nupkg
62+
--source https://nuget.pkg.github.com/Clifftech123/index.json
63+
--api-key ${{ secrets.GITHUB_TOKEN }}
64+
--skip-duplicate
65+
5966
create-release:
6067
name: Create GitHub Release
6168
needs: publish-nuget

README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
<div align="center">
2-
31
# EfCoreKit
42

5-
**EF Core extensions that eliminate boilerplate so you can focus on building features.**
3+
**EF Core extensions that eliminate boilerplate so you can focus on building features.**
64

75
[![NuGet](https://img.shields.io/nuget/v/EfCoreKit?logo=nuget&label=NuGet)](https://www.nuget.org/packages/EfCoreKit)
86
[![NuGet Downloads](https://img.shields.io/nuget/dt/EfCoreKit?logo=nuget&label=Downloads)](https://www.nuget.org/packages/EfCoreKit)
97
[![Build](https://img.shields.io/github/actions/workflow/status/Clifftech123/EfCoreKit/ci.yml?branch=develop&logo=github&label=Build)](https://github.com/Clifftech123/EfCoreKit/actions)
108
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
119

12-
**Works with any EF Core-supported database**
13-
14-
</div>
10+
*Works with any EF Core-supported database**
1511

1612
---
1713

README.nuget.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# EfCoreKit
2+
3+
EF Core extensions that eliminate boilerplate, so you can focus on building features.
4+
5+
**.NET 8 / 9 / 10** · **EF Core 8.x / 9.x / 10.x** · **Works with any EF Core-supported database**
6+
7+
---
8+
9+
## Why EfCoreKit?
10+
11+
Every .NET project with EF Core ends up writing the same plumbing: soft delete filters, audit timestamps, pagination helpers, generic repositories, transaction wrappers. EfCoreKit packages all of that into a single `AddEfCoreExtensions()` call.
12+
13+
- **Zero lock-in** Uses standard EF Core interceptors and global query filters. Your entities stay plain C# classes and you can remove EfCoreKit at any time without rewriting your data layer.
14+
- **Opt-in everything** Enable only the features you need. Nothing runs unless you turn it on.
15+
- **No custom ORM** This is not a replacement for EF Core. It's a set of extensions that plug into the pipeline you already use.
16+
17+
---
18+
19+
## Features
20+
21+
| Feature | Description |
22+
|---------|-------------|
23+
| **Base Entity Hierarchy** | Ready-made base classes: `BaseEntity`, `AuditableEntity`, `SoftDeletableEntity`, `FullEntity` |
24+
| **Entity Configuration Bases** | Fluent config base classes that auto-wire keys, indexes, and soft-delete defaults |
25+
| **Soft Delete** | Mark records as deleted with automatic global query filters; restore or hard-delete on demand |
26+
| **Audit Trail** | Auto-stamp `CreatedAt/By`, `UpdatedAt/By`; optional field-level `AuditLog` history |
27+
| **Repository + Unit of Work** | Generic `IRepository<T>` / `IReadRepository<T>` backed by `IUnitOfWork` |
28+
| **Specification Pattern** | Composable query specs with `And()` / `Or()` combinators, projection, and multi-column ordering |
29+
| **Pagination** | Offset (`ToPagedAsync`) and keyset/cursor (`ToKeysetPagedAsync`) pagination with `PagedResult<T>` |
30+
| **Dynamic Filters** | Apply runtime filter arrays (eq, ne, gt, lt, contains, in, between, isnull…) via `ApplyFilters` |
31+
| **Query Helpers** | `ExistsAsync`, `GetByIdOrThrowAsync`, `WhereIf`, `OrderByDynamic`, and more |
32+
| **DbContext Utilities** | `ExecuteInTransactionAsync`, `DetachAll`, `TruncateAsync<T>` |
33+
| **Slow Query Logging** | Logs warnings for queries exceeding a configurable threshold |
34+
| **Structured Exceptions** | `EntityNotFoundException`, `ConcurrencyConflictException`, `DuplicateEntityException`, `InvalidFilterException` |
35+
36+
---
37+
38+
## Installation
39+
40+
```bash
41+
dotnet add package EfCoreKit
42+
```
43+
44+
One package — everything is included. No separate installs needed.
45+
46+
---
47+
48+
## Quick Start
49+
50+
### 1. Register services
51+
52+
```csharp
53+
builder.Services.AddEfCoreExtensions<AppDbContext>(
54+
options => options.UseSqlServer(connectionString),
55+
kit => kit
56+
.EnableSoftDelete()
57+
.EnableAuditTrail()
58+
.UseUserProvider<HttpContextUserProvider>()
59+
.LogSlowQueries(TimeSpan.FromSeconds(1)));
60+
```
61+
62+
### 2. Inherit a base entity
63+
64+
```csharp
65+
public class Product : BaseEntity { } // int PK
66+
public class Order : AuditableEntity<Guid> { } // audited, Guid PK
67+
public class Customer : SoftDeletableEntity { } // soft-deletable + audited
68+
public class Invoice : FullEntity { } // soft-delete + audit + row version
69+
```
70+
71+
### 3. Use the repository
72+
73+
```csharp
74+
public class OrderService(IRepository<Order> repo, IUnitOfWork uow)
75+
{
76+
public async Task<Order> CreateAsync(Order order)
77+
{
78+
await repo.AddAsync(order);
79+
await uow.CommitAsync();
80+
return order;
81+
}
82+
}
83+
```
84+
85+
### 4. Use specifications
86+
87+
```csharp
88+
public class ActiveOrdersSpec : Specification<Order>
89+
{
90+
public ActiveOrdersSpec(int customerId)
91+
{
92+
AddCriteria(o => o.CustomerId == customerId);
93+
AddInclude(o => o.Items);
94+
ApplyOrderByDescending(o => o.CreatedAt);
95+
ApplyPaging(skip: 0, take: 20);
96+
ApplyAsNoTracking();
97+
}
98+
}
99+
100+
var orders = await repo.FindAsync(new ActiveOrdersSpec(customerId));
101+
```
102+
103+
---
104+
105+
## Soft Delete
106+
107+
```csharp
108+
var active = await context.Customers.ToListAsync(); // deleted rows excluded
109+
var all = await context.Customers.IncludeDeleted().ToListAsync();
110+
var deleted = await context.Customers.OnlyDeleted().ToListAsync();
111+
112+
context.Customers.Restore(customer); // un-delete
113+
context.Customers.HardDelete(customer); // permanent remove
114+
await context.SaveChangesAsync();
115+
```
116+
117+
---
118+
119+
## Pagination
120+
121+
```csharp
122+
// Offset pagination
123+
var page = await context.Orders
124+
.OrderBy(o => o.CreatedAt)
125+
.ToPagedAsync(page: 2, pageSize: 25);
126+
127+
// Keyset / cursor pagination
128+
var first = await context.Orders
129+
.OrderBy(o => o.Id)
130+
.ToKeysetPagedAsync(o => o.Id, cursor: null, pageSize: 25);
131+
132+
var next = await context.Orders
133+
.OrderBy(o => o.Id)
134+
.ToKeysetPagedAsync(o => o.Id, cursor: int.Parse(first.NextCursor!), pageSize: 25);
135+
```
136+
137+
---
138+
139+
## Dynamic Filters
140+
141+
```csharp
142+
var filters = new[]
143+
{
144+
new FilterDescriptor { Field = "Status", Operator = "eq", Value = "Active" },
145+
new FilterDescriptor { Field = "CreatedAt", Operator = "gte", Value = DateTime.UtcNow.AddDays(-30) },
146+
new FilterDescriptor { Field = "Tags", Operator = "in", Value = new[] { "VIP", "Premium" } },
147+
new FilterDescriptor { Field = "Score", Operator = "between", Value = new object[] { 10, 100 } },
148+
};
149+
150+
var results = await context.Customers.ApplyFilters(filters).ToListAsync();
151+
```
152+
153+
Supported operators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `contains`, `startswith`, `endswith`, `isnull`, `isnotnull`, `in`, `between`.
154+
155+
---
156+
157+
## Full Documentation
158+
159+
Complete guides, API reference, and examples are available on GitHub:
160+
https://github.com/Clifftech123/EfCoreKit
161+
162+
---
163+
164+
## License
165+
166+
MIT — free for personal and commercial use, forever.

src/EfCoreKit/EfCoreKit.csproj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
<PackageId>EfCoreKit</PackageId>
55
<!-- Version is managed by MinVer via git tags (e.g. v1.0.0) -->
66
<Description>
7-
EF Core extensions that eliminate boilerplate — soft delete, audit trail, multi-tenancy,
7+
EF Core extensions that eliminate boilerplate — soft delete, audit trail,
88
repository pattern, unit of work, specification pattern, offset and keyset pagination,
99
dynamic filters, DbContext utilities, and structured exceptions.
1010
Install one package and everything is included.
1111
</Description>
12-
<PackageReadmeFile>README.md</PackageReadmeFile>
12+
<PackageTags>efcore;entity-framework-core;entityframeworkcore;soft-delete;softdelete;audit-trail;audit;repository-pattern;repository;unit-of-work;specification-pattern;specification;pagination;keyset-pagination;cursor-pagination;dynamic-filters;query-helpers;data-access;orm-extensions;dotnet;csharp;boilerplate</PackageTags>
13+
<PackageReadmeFile>README.nuget.md</PackageReadmeFile>
1314
<IsPackable>true</IsPackable>
15+
<DebugType>portable</DebugType>
16+
<DebugSymbols>true</DebugSymbols>
17+
<IncludeSymbols>true</IncludeSymbols>
18+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1419
</PropertyGroup>
1520

1621
<ItemGroup>
@@ -21,7 +26,8 @@
2126
</ItemGroup>
2227

2328
<ItemGroup>
24-
<None Include="..\..\README.md" Pack="true" PackagePath="" />
29+
<None Include="..\..\README.md" Pack="false" />
30+
<None Include="..\..\README.nuget.md" Pack="true" PackagePath="" />
2531
</ItemGroup>
2632

2733
</Project>

0 commit comments

Comments
 (0)