EasyExtensions is a modular set of .NET packages for application code that tends to repeat across projects: core BCL extensions, ASP.NET Core helpers, PostgreSQL/EF Core setup, Quartz job registration, WebDAV clients, ImageSharp utilities, embedded fonts, streaming AES-GCM encryption, and a lightweight mediator.
The repository is intentionally split into small NuGet packages. Install only the package you need, keep your application dependencies narrow, and use the XML documentation in your IDE or FuGet when you need a full API reference.
- Packages
- Installation
- Quick Examples
- Configuration Notes
- Build and Test
- Releases
- Contributing
- License
| Package | Target | Use when you need |
|---|---|---|
| EasyExtensions | netstandard2.1 |
Core extensions and helpers for strings, hashes, streams, enums, dates, IP/networking, claims, random strings, queues, password hashing abstractions, Brotli HTTP helpers, and stream cipher abstractions. |
| EasyExtensions.AspNetCore | net10.0 |
ASP.NET Core helpers for exception responses, health checks, CORS, request metadata, rate limiting helpers, console logging, controllers, form files, CPU usage, and PBKDF2 password hashing registration. |
| EasyExtensions.AspNetCore.Authorization | net10.0 |
JWT authentication setup, token creation, claim building, development authorization bypass, and a base auth controller with login, refresh, logout, password, and Google login hooks. |
| EasyExtensions.AspNetCore.Sentry | net10.0 |
Sentry ASP.NET Core integration with user capture. |
| EasyExtensions.AspNetCore.Stack | net10.0 |
One-call application setup for controllers, logging, compression, Quartz, SignalR, CORS, health checks, optional PostgreSQL, optional authorization, and optional EasyVault secrets. |
| EasyExtensions.Clients | net10.0 |
Cached IP lookup helpers backed by ipapi.co and configurable GeoIP endpoints. |
| EasyExtensions.Crypto | net10.0 |
Streaming AES-GCM encryption/decryption, per-chunk authentication, HKDF subkeys, secure random bytes, and hash helpers. |
| EasyExtensions.Drawing | net10.0 |
ImageSharp helpers for JPEG conversion, drawing text, blurred backgrounds, automatic brightness adjustment, and font integration. |
| EasyExtensions.EntityFrameworkCore | net10.0 |
Audited entities and DbContext base types, Gridify mapper registration, database health checks, and migration helpers. |
| EasyExtensions.EntityFrameworkCore.Npgsql | net10.0 |
PostgreSQL DbContext registration, connection string construction from configuration, lazy loading options, and design-time factory registration. |
| EasyExtensions.Fonts | netstandard2.1 |
Embedded fonts: Arial, Consola, FreeMonospaced, RetroGaming, and UbuntuMono. |
| EasyExtensions.Mediator | netstandard2.1 |
A MediatR v12.5.0 based mediator package with request, notification, stream request, pipeline behavior, pre/post processor, and exception processor support. |
| EasyExtensions.Quartz | netstandard2.1 |
Reflection-based Quartz job registration with JobTriggerAttribute, hosted service setup, and optional PostgreSQL persistent store. |
| EasyExtensions.WebDav | netstandard2.1 |
WebDAV and Nextcloud file operations: folders, existence checks, uploads, listings, downloads, and deletes. |
| EasyExtensions.Windows | netstandard2.1 |
Windows-specific helpers for shortcuts and moving files or directories to the recycle bin. |
Install packages individually:
dotnet add package EasyExtensions
dotnet add package EasyExtensions.AspNetCore
dotnet add package EasyExtensions.Crypto
dotnet add package EasyExtensions.EntityFrameworkCore.NpgsqlFor an opinionated ASP.NET Core setup, start with:
dotnet add package EasyExtensions.AspNetCore.Stackusing EasyExtensions.Extensions;
using EasyExtensions.Helpers;
using System.Net;
string digest = "hello".Sha512();
IPAddress network = IPAddress.Parse("192.168.10.25").GetNetwork(24);
string maskedEmail = StringHelpers.HideEmail("vadim@example.com");using EasyExtensions.Clients;
var defaultGeoIp = await GeoIpClient.Shared.LookupAsync("8.8.8.8");
var bridgeGeoIpClient = new GeoIpClient("https://bridge.cottoncloud.dev/api/v1/lookup");
var bridgeGeoIp = await bridgeGeoIpClient.LookupAsync("8.8.8.8");using EasyExtensions.AspNetCore.Extensions;
builder.Logging.AddSimpleConsoleLogging();
builder.Services
.AddDefaultHealthChecks()
.AddDefaultCorsWithOrigins("https://app.example.com")
.AddExceptionHandler()
.AddPbkdf2PasswordHashService();using EasyExtensions.AspNetCore.Authorization.Extensions;
builder.Services.AddJwt(useCookies: true);{
"JwtSettings": {
"Key": "0123456789abcdef0123456789abcdef",
"Issuer": "my-api",
"Audience": "my-clients",
"LifetimeMinutes": 60
}
}using EasyExtensions.AspNetCore.Stack.Extensions;
builder.AddEasyStack(stack => stack
.WithPostgres<AppDbContext>(useLazyLoadingProxies: false)
.AddAuthorization()
.UseSecrets(useSecrets: true));using EasyExtensions.EntityFrameworkCore.Npgsql.Extensions;
builder.Services.AddPostgresDbContext<AppDbContext>(postgres =>
{
postgres.ConfigurationSection = "DatabaseSettings";
postgres.UseLazyLoadingProxies = false;
});{
"DatabaseSettings": {
"Host": "localhost",
"Port": "5432",
"Username": "postgres",
"Password": "postgres",
"Database": "app"
}
}using EasyExtensions.Quartz.Attributes;
using EasyExtensions.Quartz.Extensions;
using Quartz;
[JobTrigger(minutes: 5, startNow: true)]
public sealed class CleanupJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
return Task.CompletedTask;
}
}
builder.Services.AddQuartzJobs();using EasyExtensions.Crypto;
using System.Security.Cryptography;
byte[] masterKey = RandomNumberGenerator.GetBytes(AesGcmStreamCipher.KeySize);
using var cipher = new AesGcmStreamCipher(masterKey, memoryLimitBytes: 256L * 1024 * 1024);
await using var input = File.OpenRead("plain.bin");
await using var encrypted = File.Create("plain.bin.eegcm");
await cipher.EncryptAsync(input, encrypted);
await using var cipherText = File.OpenRead("plain.bin.eegcm");
await using var plainText = File.Create("plain-restored.bin");
await cipher.DecryptAsync(cipherText, plainText);using EasyExtensions.WebDav;
using var client = WebDavCloudClient.CreateNextcloudClient(
"https://cloud.example.com",
username: "user",
password: "app-password");
await client.CreateFolderAsync("backups");
using var backup = File.OpenRead("backup.zip");
await client.UploadFileAsync(backup, "backups/backup.zip");- The full solution currently uses the .NET 10 SDK. Some packages still target
netstandard2.1; see the package table before choosing a package for older applications. EasyExtensions.AspNetCore.Authorizationaccepts either aJwtSettingssection or flatJwtKey,JwtIssuer,JwtAudience, andJwtLifetimeMinuteskeys. Configure a persistent signing key in production.EasyExtensions.AspNetCore.Stackallows all CORS origins whenCorsOriginsis missing. SetCorsOriginsexplicitly for production services.EasyExtensions.AspNetCore.SentryenablesSendDefaultPiiwhen Sentry is active. Review this against your data handling policy.EasyExtensions.Cryptoexpects you to own key storage and rotation. Do not hard-code master keys in source control.EasyExtensions.WebDavcurrently changesServicePointManager.ServerCertificateValidationCallback; review this before production use.
git clone https://github.com/bvdcode/EasyExtensions.git
cd EasyExtensions/Sources
dotnet restore
dotnet build --configuration Release
dotnet test --configuration ReleaseThe test projects use NUnit and target net10.0.
Releases are produced by GitHub Actions from main when files under Sources/ change. The workflow builds the solution, packs NuGet packages, publishes artifacts, pushes packages to GitHub Packages and NuGet.org, and creates a GitHub release. Versioning is driven by GitVersion; the current next-version is configured in GitVersion.yml.
Contributions are welcome. For a clean pull request:
- Fork the repository.
- Create a focused feature branch.
- Add or update tests for behavior changes.
- Run
dotnet testfromSources/. - Update this README when package scope, setup, or public examples change.
- Open a pull request with a clear description of the change.
Issues and feature requests are also welcome. Small, well-scoped proposals are easiest to review.
Most packages are distributed under the MIT License. See LICENSE.md.
EasyExtensions.Mediator is based on MediatR v12.5.0 and uses Apache-2.0 package licensing. See Sources/EasyExtensions.Mediator/LICENSE.md.
Created and maintained by Vadim Belov.