![]() |
VOOZH | about |
dotnet add package DRN.Framework.Utils --version 0.9.5
NuGet\Install-Package DRN.Framework.Utils -Version 0.9.5
<PackageReference Include="DRN.Framework.Utils" Version="0.9.5" />
<PackageVersion Include="DRN.Framework.Utils" Version="0.9.5" />Directory.Packages.props
<PackageReference Include="DRN.Framework.Utils" />Project file
paket add DRN.Framework.Utils --version 0.9.5
#r "nuget: DRN.Framework.Utils, 0.9.5"
#:package DRN.Framework.Utils@0.9.5
#addin nuget:?package=DRN.Framework.Utils&version=0.9.5Install as a Cake Addin
#tool nuget:?package=DRN.Framework.Utils&version=0.9.5Install as a Cake Tool
👁 master
👁 develop
👁 Quality Gate Status
👁 Security Rating
👁 Maintainability Rating
👁 Reliability Rating
👁 Vulnerabilities
👁 Bugs
👁 Lines of Code
👁 Coverage
Core utilities package providing attribute-based dependency injection, configuration management, scoped logging, ambient context, and essential extensions.
[Scoped<T>], [Singleton<T>], [Transient<T>] for zero-config service registrationIAppSettings with typed access, [Config("Section")] bindingsIScopedLog aggregates structured logs per requestICancellationUtils for request lifecycle controlJpegValidatorNumberBuilder for custom data structuresScopeContext.UserId, ScopeContext.Settings anywhereAddServicesWithAttributes() scans and registers all attributed servicesParseRegister and use a service with attribute-based DI:
// 1. Define your service with DI attribute
public interface IGreetingService { string Greet(string name); }
[Scoped<IGreetingService>]
public class GreetingService : IGreetingService
{
public string Greet(string name) => $"Hello, {name}!";
}
// 2. Register all attributed services in Startup
services.AddServicesWithAttributes();
// 3. Inject and use
public class HomeController(IGreetingService greetingService) : Controller
{
public IActionResult Index() => Ok(greetingService.Greet("World"));
}
Complete example with configuration binding, scoped logging, and ambient context:
// Bind configuration section to strongly-typed class
[Config]
public class PaymentSettings
{
public string ApiKey { get; set; } = "";
public int TimeoutSeconds { get; set; } = 30;
}
// Service using scoped logging and settings
[Scoped<IPaymentService>]
public class PaymentService(IAppSettings settings, IScopedLog log, PaymentSettings config) : IPaymentService
{
public async Task<PaymentResult> ProcessAsync(decimal amount)
{
// Track execution time
using var duration = log.Measure("PaymentProcessing");
// Add structured context
log.Add("Amount", amount);
log.AddToActions("Processing payment");
// Access ambient data anywhere
var userId = ScopeContext.UserId;
// Use typed configuration
if (config.TimeoutSeconds < 10)
throw ExceptionFor.Configuration("Timeout too short");
return new PaymentResult(Success: true);
}
}
If you are using DRN.Framework.Hosting (inheriting from DrnProgramBase), this package is automatically registered and validated.
For manual installation (e.g. Console Apps, Workers):
// Registers attributes, HybridCache, and TimeProvider
builder.Services.AddDrnUtils();
AddDrnUtils() registers Microsoft's HybridCache with default in-memory caching. To configure distributed caching (e.g., Redis), add your IDistributedCache registration before calling AddDrnUtils():
// Optional: Add distributed cache backend
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
// HybridCache will use the distributed cache if available
builder.Services.AddDrnUtils();
For DRN Hosting rate limiting, use HybridCache to cache tenant plan, feature flag, or quota policy data. Do not treat HybridCache / IDistributedCache as an atomic distributed rate-limit counter by itself; hard multi-instance quotas need a backend designed for atomic operations, such as Redis with server-side Lua scripts, or enforcement at an API gateway/CDN/WAF layer.
Reduce configuration boilerplate by using attributes directly on services. AddServicesWithAttributes() scans the calling assembly by default; pass an Assembly argument to scan a specific target assembly.
| Attribute | Lifetime | Usage |
|---|---|---|
[Singleton<T>] |
Singleton | [Singleton<IMyService>] public class MyService : IMyService |
[Scoped<T>] |
Scoped | [Scoped<IMyService>] public class MyService : IMyService |
[Transient<T>] |
Transient | [Transient<IMyService>] public class MyService : IMyService |
[SingletonWithKey<T>] |
Singleton (Keyed) | [SingletonWithKey<IMyService>("key")] |
[ScopedWithKey<T>] |
Scoped (Keyed) | [ScopedWithKey<IMyService>("key")] |
[TransientWithKey<T>] |
Transient (Keyed) | [TransientWithKey<IMyService>("key")] |
[HostedService] |
Singleton | [HostedService] public class MyWorker : BackgroundService |
[Config] |
Singleton | [Config("Section")] public class MySettings |
[ConfigRoot] |
Singleton | [ConfigRoot] public class RootSettings |
All lifetime attributes accept an optional tryAdd parameter (default: true). When true, TryAdd is used so existing registrations are not overwritten. Set to false to allow multiple implementations of the same service type.
Use [HostedService] to register IHostedService/BackgroundService implementations without manual AddHostedService<T>() calls. The class must implement IHostedService; otherwise the attribute is silently ignored.
[HostedService]
public class MyBackgroundWorker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Do periodic work
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
DrnProgramBase automatically runs this validation at startup.
ValidateServicesAddedByAttributesAsync().// In Program.cs
await app.Services.ValidateServicesAddedByAttributesAsync();
In integration tests with DRN.Framework.Testing:
[Theory, DataInline]
public async Task Validate_Dependencies(DrnTestContext context)
{
context.ServiceCollection.AddServicesWithAttributes(); // Register local assembly
await context.ValidateServicesAsync(); // Verifies attribute-registered services can be resolved
}
Manage request-scoped cancellation tokens using ICancellationUtils. It supports merging tokens from multiple sources, such as HttpContext.RequestAborted and application-level timeouts.
public class MyScopedService(ICancellationUtils cancellation)
{
public async Task DoWorkAsync(CancellationToken externalToken)
{
// Automatically merges with the scoped token
cancellation.Merge(externalToken);
// Use the unified token
await SomeAsyncOp(cancellation.Token);
if (cancellation.IsCancellationRequested)
return;
}
}
Services can require complex registration logic or post-startup actions. Attributes inheriting from ServiceRegistrationAttribute handle this.
Example: DrnContext<T> (in DRN.Framework.EntityFramework) is decorated with [DrnContextServiceRegistration], which:
PostStartupValidationAsync).// The base class DrnContext handles the registration attributes.
// You just inherit from it, and your context is auto-registered with migration support.
public class MyDbContext : DrnContext<MyDbContext> { }
Access configuration using strongly-typed environment checks and utility methods.
public class MyService(IAppSettings settings)
{
public void DoWork()
{
if (settings.IsDevelopmentEnvironment) { /* dev-only logic */ }
if (settings.IsStagingEnvironment) { /* staging-only logic */ }
var conn = settings.GetRequiredConnectionString("Default");
var value = settings.GetValue<int>("MySettings:Timeout", 30);
var debugSummary = settings.GetDebugView().ToSummary(); // redacts secret-looking values by default
}
}
GetDebugView(includeRawValues: true) only includes raw values in Development. Secret-looking keys and sections are redacted by default in summaries. Child keys remain listed even when a provider also defines a scalar value for the parent section, and summary paths use the value provider's key casing. Object-based configuration helpers serialize through the framework JSON defaults and therefore use camelCase keys; explicit key/value configuration preserves the key text supplied by the caller.
[Config])Bind classes directly to configuration sections. These are registered as Singletons.
[Config("PaymentSettings")] // Binds to "PaymentSettings" section
public class PaymentOptions
{
public string ApiKey { get; set; }
}
[Config] // Binds to "FeatureFlags" section (class name)
public class FeatureFlags { ... }
[ConfigRoot] // Binds to root configuration
public class RootSettings { ... }
The framework automatically loads configuration in this order:
appsettings.jsonappsettings.{Environment}.jsonASPNETCORE_, DOTNET_, then unprefixed)/appconfig/key-per-file-settings/*/appconfig/json-settings/*.jsonOverride the mount directory by registering IMountedSettingsConventionsOverride.
| Symptom | Cause | Solution |
|---|---|---|
ConfigurationException on startup |
Missing required configuration key | Add the key to appsettings.json or environment variables |
GetRequiredConnectionString throws |
Connection string not found | Verify key exists under ConnectionStrings section |
IsDevelopmentEnvironment always false |
ASPNETCORE_ENVIRONMENT not set |
Set environment variable or use launchSettings.json |
| Mounted settings not loading | Wrong mount path | Verify files exist at /appconfig/json-settings/ or override via IMountedSettingsConventionsOverride |
| Environment variables not binding | Wrong naming format | Use __ (double underscore) for nested keys: MySection__MyKey |
Feature flags and runtime knobs bound from the DrnAppFeatures configuration section via [Config].
{
"DrnAppFeatures": {
"SeedData": false,
"SeedKey": "Peace at home! Peace in the world! - Mustafa Kemal Atatürk (1931)",
"DisableRequestBuffering": false,
"MaxRequestBufferingSize": 0,
"DrnRateLimit": {
"Disabled": false,
"TokenLimit": 100,
"ReplenishmentSeconds": 60,
"TokensPerPeriod": 100,
"PreAuthTokenLimit": 1000,
"PreAuthReplenishmentSeconds": 60,
"PreAuthTokensPerPeriod": 1000,
"PostAuthTokenLimit": 0,
"PostAuthReplenishmentSeconds": 0,
"PostAuthTokensPerPeriod": 0
}
}
}
DrnRateLimit is the configuration key; application code reads the same settings through IAppSettings.Features.RateLimit.
Shared values apply to both DRN Hosting rate limiting phases. Phase-specific values set to 0 inherit the shared value; positive phase-specific values override it. Treat these values as global defaults; tenant plan, feature-flag, and account-specific quotas belong in DRN Hosting rate-limit rules. See the for operational guidance, endpoint metadata behavior, and production scaling notes.
Nested option objects must be validated explicitly before relying on child data annotations for startup safety. DrnAppFeatures validates DrnRateLimit as part of root validation because plain Validator.TryValidateObject does not recursively walk nested objects by itself.
| Property | Type | Default | Description |
|---|---|---|---|
ApplicationStartedBy |
string? |
null |
Identifies which test started the application (set automatically by DrnTestContext). |
SeedData |
bool |
false |
Enables data seeding on startup. |
SeedKey |
string |
"Peace at home!…" |
Secret key for seed operations. Enforced [SecureKey(MinLength = 58)]. |
InternalRequestHttpVersion |
string |
"1.1" |
HTTP version used by IInternalRequest. |
InternalRequestProtocol |
string |
"http" |
Protocol scheme used by IInternalRequest (e.g., http, https). |
UseMonotonicDateTimeProvider |
bool |
false |
Reserved experimental flag for monotonic time-provider behavior data; it is not wired as a provider switch. |
DisableRequestBuffering |
bool |
false |
Disables request body buffering entirely. Use for high-throughput services (e.g., file upload endpoints). |
MaxRequestBufferingSize |
int |
0 (→ 30,000) |
Maximum request body size to buffer in bytes. Values below 10,000 are ignored; 0 uses the 30,000-byte default. |
DrnRateLimit.Disabled |
bool |
false |
Disables both pre-auth and post-auth DRN Hosting rate limiting layers. |
DrnRateLimit.PartitionLogMode |
RateLimitPartitionLogMode |
KeyedHash |
Controls rejected IP/partition logging. KeyedHash logs deterministic keyed hashes for correlation; PlainText logs raw values and should be limited to controlled development or dedicated audit sinks. |
DrnRateLimit.TokenLimit |
int |
100 |
Token bucket burst capacity. Must be positive. |
DrnRateLimit.ReplenishmentSeconds |
int |
60 |
Token replenishment period in seconds. Must be positive. |
DrnRateLimit.TokensPerPeriod |
int |
100 |
Tokens added per replenishment period. Must be positive. |
DrnRateLimit.PreAuthTokenLimit |
int |
1000 |
Coarse pre-auth burst capacity for shared B2B NAT/VPN/CDN egress addresses. 0 inherits TokenLimit. |
DrnRateLimit.PreAuthReplenishmentSeconds |
int |
60 |
Pre-auth replenishment period. 0 inherits ReplenishmentSeconds. |
DrnRateLimit.PreAuthTokensPerPeriod |
int |
1000 |
Pre-auth tokens per period. 0 inherits TokensPerPeriod. |
DrnRateLimit.PostAuthTokenLimit |
int |
0 |
Optional post-auth burst capacity. 0 inherits TokenLimit. |
DrnRateLimit.PostAuthReplenishmentSeconds |
int |
0 |
Optional post-auth replenishment period. 0 inherits ReplenishmentSeconds. |
DrnRateLimit.PostAuthTokensPerPeriod |
int |
0 |
Optional post-auth tokens per period. 0 inherits TokensPerPeriod. |
Request buffering and rate limiting settings are consumed by DRN.Framework.Hosting. See the for middleware details.
NexusAppSettings provides Nexus routing, source-known ID generator identity, secure/plain ID mode, and the MAC key ring used by SourceKnownEntityIdUtils.
{
"NexusAppSettings": {
"NexusAddress": "localhost:5988",
"AppId": 5,
"AppInstanceId": 12,
"UseSecureSourceKnownIds": true,
"MacKeys": [
{
"Key": "0123456789abcdef0123456789abcdef",
"Format": "Utf8",
"Default": true
}
]
}
}
MacKeys must contain exactly one default key. Generation always uses the default key. Parsing tries the default key first and then the remaining configured keys, so old IDs remain parseable during key rotation while the previous key stays in the key ring.
ByteEncoding |
Requirement |
|---|---|
Utf8 |
Default when omitted. Key must be exactly 32 UTF-8 bytes. ASCII 32-character values satisfy this; non-ASCII values are valid only when the UTF-8 byte count is exactly 32. |
Hex |
Key must hex-decode to exactly 32 bytes, normally 64 hex characters. A 32-character hex string is rejected because it decodes to 16 bytes. |
Base64 |
Key must Base64-decode to exactly 32 bytes. |
Base64UrlEncoded |
Key must Base64Url-decode to exactly 32 bytes. This is the format produced by the framework Hash() default. |
Invalid user-provided keys are not hashed, stretched, truncated, repaired, or treated as another format. Startup validation rejects malformed encodings, empty keys, wrong decoded lengths, and raw values that are not exactly 32 UTF-8 bytes. Exception messages avoid including the secret key value.
When no default MAC key is configured in the Development environment, AppSettings adds one automatically. This key is deterministic from DrnAppFeatures.SeedKey, not random, and is stored in memory as Format = Base64UrlEncoded.
IScopedLog)IScopedLog provides request-scoped structured logging. It aggregates operational data, metrics, and checkpoints during the request lifecycle, flushing them as a single entry for efficient monitoring.
TraceId, UserId, RequestPath, and custom scope data.public class OrderService(IScopedLog logger)
{
public void ProcessOrder(int orderId)
{
// 1. Measure execution time and count
// Automatically tracks duration and increments "Stats_ProcessOrder_Count"
using var _ = logger.Measure("ProcessOrder");
// 2. Add structured data (Key-Value)
logger.Add("OrderId", orderId);
logger.AddIfNotNullOrEmpty("Referrer", "PartnerA");
// 3. Track execution checkpoints
logger.AddToActions("Validating order");
try
{
// ... logic ...
// 4. Flatten and add complex objects
logger.AddProperties("User", new { Name = "John", Role = "Admin" });
}
catch(Exception ex)
{
// 5. Log exception but keep the request contextual log intact
logger.AddException(ex, "Failed to process order");
}
}
}
IExternalRequest, IInternalRequest)Wrappers around Flurl for HTTP clients with standardized JSON conventions and HTTP version policy configuration. Retries/circuit breakers are not configured by this package.
Use IExternalRequest for standard external API calls. It pre-configures DefaultJsonSerializer and enforces HTTP version policies.
public class PaymentService(IExternalRequest request)
{
public async Task Process()
{
// Enforces exact HTTP version for better compatibility with modern APIs
var response = await request.For("https://api.example.com", HttpVersion.Version11)
.AppendPathSegment("v1/charges")
.PostJsonAsync(new { Amount = 1000 })
.ToJsonAsync<ExternalApiResponse>();
}
}
Use IInternalRequest for Service-to-Service communication in Kubernetes. It's designed to work with Linkerd, supporting automatic protocol switching (HTTP/HTTPS) based on infrastructure settings.
Instead of using IInternalRequest directly in business logic, wrap it in a typed request factory for better maintainability and configuration encapsulation.
// 1. Definition (External Factory Wrapper)
public interface INexusRequest { IFlurlRequest For(string path); }
[Singleton<INexusRequest>]
public class NexusRequest(IInternalRequest request, IAppSettings settings) : INexusRequest
{
private readonly string _nexusAddress = settings.NexusAppSettings.NexusAddress;
public IFlurlRequest For(string path) => request.For(_nexusAddress).AppendPathSegment(path);
}
// 2. Client Usage
public class NexusClient(INexusRequest request) : INexusClient
{
public async Task<HttpResponse<string>> GetStatusAsync() =>
await request.For("status").GetAsync().ToStringAsync();
}
ScopeContext provides ambient access to request-scoped data. This simplifies cross-cutting concerns like auditing, multi-tenancy, and security by avoiding deep parameter passing especially in Razor Pages(.cshtml) files.
UserId, TraceId, and Authenticated status anywhere.IAppSettings, IScopedLog, and IServiceProvider.ScopeContext.InitializeForTest(...) resets the async-local scope before seeding test services, user, log, and trace data.var currentUserId = ScopeContext.UserId;
var traceId = ScopeContext.TraceId;
var settings = ScopeContext.Settings; // Static IAppSettings access
var logger = ScopeContext.Log; // Static IScopedLog access
if (ScopeContext.IsUserInRole("Admin")) { ... }
EncodingExtensions)Unified API for binary-to-text encodings and model serialization-encoding.
model.Encode(ByteEncoding.Hex) and hexString.Decode<TModel>().HashExtensions)High-performance hashing extensions supporting modern and legacy algorithms.
HashWithKey) for integrity protection.BinaryData; prefer these overloads for file and upload hashing.JsonMergePatch.SafeApplyMergePatch follows RFC 7386 for partial updates with built-in recursion depth protection.QueryParameterSerializer flattens complex nested objects/arrays into clean query strings for API clients.model.Serialize(method) supports both JSON and Query String formats.ToBinaryDataAsync and ToArrayAsync extensions with MaxSizeGuard to prevent memory exhaustion from untrusted streams.Extensions for programmatic validation using System.ComponentModel.DataAnnotations.
DRN.Framework.SharedKernel.ValidationException for standardized error reporting across layers.The framework provides IPaginationUtils for high-performance, monotonic cursor-based pagination. It leverages the temporal sequence of SourceKnownEntityId to ensure stable results even as data is being added.
public class OrderService(IPaginationUtils pagination)
{
public async Task<PaginationResult<Order>> GetRecentOrdersAsync(PaginationRequest request)
{
var query = dbContext.Orders.Where(x => x.Active);
return await pagination.GetResultAsync(query, request);
}
}
For scenarios requiring custom ID generation or compact binary data structures, use NumberBuilder and NumberParser. NumberBuilder<TNumber> is a ref struct; NumberParser is a value-type parser for low-allocation bit manipulation.
// Use NumberBuilder to pack data into a long
var builder = NumberBuilder.GetLong();
builder.TryAddNibble(0x05); // Add 4 bits
builder.TryAddUShort(65535); // Add 16 bits
long packedValue = builder.GetValue();
// Use NumberParser to unpack
var parser = NumberParser.Get(packedValue);
byte nibble = parser.ReadNibble();
ushort value = parser.ReadUShort();
// Multi-format serialization
var json = model.Serialize(SerializationMethod.SystemTextJson);
var query = model.Serialize(SerializationMethod.QueryString);
// Data Integrity
var hash = data.Hash(HashAlgorithm.Blake3);
var fileHash = fileStream.Hash(HashAlgorithm.Sha256);
// Secure stream conversion
var bytes = await requestStream.ToBinaryDataAsync(maxSize: 1024 * 1024);
Reusable validators live under DRN.Framework.Utils.Validators.
using DRN.Framework.Utils.Validators;
var validation = await JpegValidator.ValidateAsync(requestStream, maxLength: 1024 * 1024);
if (!validation.IsValid)
{
var message = validation.ErrorReason switch
{
JpegValidationErrorReason.MaxLengthExceeded => "Profile picture exceeds the maximum allowed size.",
JpegValidationErrorReason.InvalidMaxLength => "Profile picture maximum size must be zero or greater.",
_ => "Profile picture must be a valid JPEG image."
};
throw ExceptionFor.Validation(message);
}
var imageBytes = validation.ImageData;
JpegValidator performs structural JPEG checks for markers, segment bounds, frame metadata, scan metadata, scan data presence, and optional maximum byte length. JpegValidationResult.ErrorReason distinguishes MaxLengthExceeded, InvalidMaxLength, and InvalidJpeg failures. Use ValidateAsync when validating an upload stream and keeping the validated bytes for persistence.
Track database migration status and pending model changes in real-time during development.
public class StartupService(DevelopmentStatus status, IScopedLog log)
{
public void CheckStatus()
{
if (status.HasPendingChanges)
{
log.AddToActions("Warning: Pending database changes detected");
foreach (var model in status.Models)
{
model.LogChanges(log, "Development");
}
}
}
}
TimeStampManager)For systems requiring frequent timestamp lookups (like ID generation or rate limiting), TimeStampManager provides a cached UTC timestamp updated periodically (default 10ms) to reduce DateTimeOffset.UtcNow overhead.
long precisionTicks = TimeStampManager.CurrentTimestamp(EpochTimeUtils.DefaultEpoch);
DateTimeOffset now = TimeStampManager.UtcNow; // Cached UTC time truncated to 250ms precision
RecurringAction)A lock-free, atomic timer implementation that prevents overlapping executions if one cycle takes longer than the period.
var worker = new RecurringAction(async () => {
await DoHeavyWork();
}, period: 1000, start: true);
worker.Stop();
SourceKnownEntity ID's provide reversible, type-safe, and integrity-checked identifiers.
ID generation is automatically handled by DrnContext when SourceKnownEntities are saved.
The Generate method dispatches to secure or plain generation based on the UseSecureSourceKnownIds flag in NexusAppSettings (defaults to true). Explicit GenerateSecure and GeneratePlain methods are also available to bypass the flag.
| Method | Behavior |
|---|---|
Generate |
Dispatches to secure or plain based on UseSecureSourceKnownIds |
GenerateSecure |
AES-256-ECB encrypted — full 16-byte GUID is a ciphertext block |
GeneratePlain |
Plaintext with visible 8D8D version/variant markers |
ToSecure |
Converts a plain ID to its secure form (idempotent) |
ToPlain |
Converts a secure ID to its plain form (idempotent) |
Secure variant encrypts the entire 16-byte GUID using AES-256-ECB as a pseudo-random permutation (PRP). For a single 128-bit block, ECB is mathematically identical to CBC with a zero IV — no nonce required, no nonce-reuse vulnerability. Key separation ensures BLAKE3 keyed MAC (integrity) and AES-256 (confidentiality) use cryptographically independent keys from the same keyring entry.
Generation uses the default NexusMacKey. Parse uses a default-first key-ring fallback, so IDs generated before key rotation can still be parsed while the previous key remains configured.
Thread-Safety Verification: Although MSDN documents Aes instance members as not guaranteed thread-safe, the span-based one-shot methods EncryptEcb and DecryptEcb are stateless and fully safe for concurrent execution by this singleton service:
BCryptEncrypt under thread-safe CNG key handles.CCCrypt function directly.SafeEvpCipherCtxHandle context inside the call, avoiding instance state mutation.
Concurrency has been validated via stress tests (SourceKnownEntityIdUtils_Should_Generate_Ids_For_3_Seconds) executing ~800,000 parallel operations without data races or corruption.Post-quantum readiness: AES-256 retains 128-bit security under Grover's algorithm — NIST recommended for post-quantum symmetric encryption.
// Generate with flag-based dispatch (secure by default)
var entityId = sourceKnownEntityIdUtils.Generate<User>(id);
// Explicitly secure
var secureId = sourceKnownEntityIdUtils.GenerateSecure<User>(id);
// Explicitly plain (visible markers for debugging/development)
var plainId = sourceKnownEntityIdUtils.GeneratePlain<User>(id);
// Convert between secure and plain forms (idempotent)
var convertedSecureId = sourceKnownEntityIdUtils.ToSecure(plainEntityId);
var convertedPlainId = sourceKnownEntityIdUtils.ToPlain(secureEntityId);
Parse auto-detects encrypted and plaintext IDs — it first checks for plaintext markers, then attempts AES-ECB decryption if markers are absent. Both paths verify MAC integrity.
Add rate limiting to endpoints that accept SourceKnownEntityId from untrusted sources to prevent brute-force attacks.
Users can validate incoming IDs (e.g., from APIs) using multiple approaches depending on the context:
1. Injectable Utility (Recommended for Service Layer)
var sourceKnownId = sourceKnownEntityIdUtils.Validate<User>(externalGuidId);
2. SourceKnownRepository (Recommended for Data Access)
// Method on SourceKnownRepository<TEntity>
var sourceKnownId = userRepository.GetEntityId(externalGuidId);
3. SourceKnownEntity (Recommended for Domain Logic)
// Helper on SourceKnownEntity base class
var sourceKnownId = userInstance.GetEntityId<User>(externalGuidId);
Each SourceKnownEntityId (SKEID) packs identity, integrity, time-addressing, and UUID V8 compatibility (RFC 9562 §5.8) into a single 128-bit GUID:
| Byte(s) | Purpose |
|---|---|
| 0 | Epoch index (8 bits — up to 256 epochs) |
| 1–4 | SKID upper half (32 bits, sign-toggled) |
| 5 | SKID low byte 0 (MSB of SKID lower half / timestamp LSB) |
| 6 | Version marker (0x8D — UUID V8, RFC 9562 §5.8) |
| 7 | Entity type (8 bits — up to 256 entity types) |
| 8 | Variant marker (0x8D — RFC 4122 compatible) |
| 9–11 | SKID low bytes (remaining 24 bits) |
| 12–15 | BLAKE3 keyed MAC (32 bits — integrity verification) |
SourceKnownEntityIds use epoch-based time addressing for monotonic ordering. Each epoch spans approximately 68 years ($2^{31}$ seconds total coverage, split across two halves), starting from 2025-01-01.
| Property | Value |
|---|---|
| Epoch start | 2025-01-01 |
| Single epoch duration | ~68 years ($2^{31}$ seconds) |
| Maximum epochs | 256 (byte 0) |
| Total address space | ~17,421 monotonic years |
The first epoch requires no configuration and covers approximately 68 years from 2025-01-01. Epoch transitions are handled automatically.
TimeProvider singleton is registered by default to TimeProvider.System for testable time entry. See Time & Async for high-performance alternatives.
LockUtils)LockUtils provides static helpers for lock-free atomic operations built on Interlocked. Use these primitives to coordinate concurrent access without OS-level locks.
| Method | Purpose |
|---|---|
TryClaimLock(ref int) |
Atomically claims a lock (0 → 1). Returns true if successful. |
TryClaimScope(ref int) |
Returns a disposable LockScope that auto-releases on dispose. |
ReleaseLock(ref int) |
Unconditionally releases a lock (→ 0). |
TrySetIfEqual<T>(ref T?, T, T?) |
Atomic CAS for reference types; sets value if current equals comparand. |
TrySetIfNull<T>(ref T?, T) |
Sets value only if current is null. |
TrySetIfNotEqual<T>(ref T?, T, T?) |
Sets value only if current does not equal comparand (retry loop). |
TrySetIfNotNull<T>(ref T?, T) |
Sets value only if current is not null. |
// Disposable lock scope (preferred) — auto-releases on dispose
private int _lock;
using var scope = LockUtils.TryClaimScope(ref _lock);
if (scope.Acquired) { /* critical section */ }
// One-time initialization guard
private MyService? _instance;
var service = new MyService();
LockUtils.TrySetIfNull(ref _instance, service);
Comprehensive set of extensions for standard .NET types and reflection.
MethodUtilsHighly optimized reflection helpers with built-in caching for generic and non-generic method invocation.
instance.InvokeMethod("Name", args) and type.InvokeStaticMethod("Name", args).instance.InvokeGenericMethod("Name", typeArgs, args) with static and uncached variations.ConcurrentDictionary and record struct keys for zero-allocation cache lookups.Advanced DI container manipulation for testing and modularity.
sc.GetAllAssignableTo<TService>() retrieves all descriptors matching a type.ReplaceScoped, ReplaceSingleton, and ReplaceInstance for mocking/overriding dependencies in integration tests.ToSnakeCase, ToCamelCase, and ToPascalCase for clean code-to-external system mapping.string.Parse<T>() and string.TryParse<T>(out result) using the modern IParsable<T> interface.NormalizeDirectoryPath() resolves a directory path to a full path and trims trailing separators without trimming filesystem roots. IsPathWithinDirectory() performs full-path containment checks.ToStream() and ToByteArray() shortcuts with UTF8 default.GetLines() for IFileInfo with efficient physical path reading.assembly.GetSubTypes(typeof(T)) and assembly.GetTypesAssignableTo(to).assembly.CreateSubTypes<T>() automatically discovers and instantiates classes with parameterless constructors.type.GetAssemblyName() returns a clean assembly name.PrepareScopeLogForFlurlExceptionAsync() captures exhaustive request/response metadata from Flurl exceptions into IScopedLog.GetGatewayStatusCode() maps API errors to standard gateway codes (502, 503, 504).ClearFilteredSetups() utility for complex test scenarios.instance.GetGroupedPropertiesOfSubtype(type) recursively finds properties matching a base type across complex object graphs.IDictionary to handle null-safe value retrieval and manipulation.GetBitPositions() for long values and bitmask generators for signed/unsigned lengths.// Discovery and Instantiation
var implementations = typeof(IMyInterface).Assembly.CreateSubTypes<IMyInterface>();
// Modern Parsing
int value = "123".Parse<int>();
// Casing for APIs
var key = "MyPropertyName".ToSnakeCase(); // my_property_name
global using DRN.Framework.SharedKernel;
global using DRN.Framework.Utils.DependencyInjection;
For complete examples, see Sample.Hosted.
Documented with the assistance of DiSC OS
Semper Progressivus: Always Progressive
Author: Duran Serkan KILIÇ
Date: 2026-06-14 21:30:19 +0300
Hash: ebe902574f06c0a2c2c0d8b4b2e28aafbfe418a6
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 2 NuGet packages that depend on DRN.Framework.Utils:
| Package | Downloads |
|---|---|
|
DRN.Framework.EntityFramework
DRN.Framework.EntityFramework provides DrnContext with conventions to develop rapid and effective domain models. ## Commit Info Author: Duran Serkan KILIÇ Date: 2026-06-14 21:30:19 +0300 Hash: ebe902574f06c0a2c2c0d8b4b2e28aafbfe418a6 |
|
|
DRN.Framework.Hosting
DRN.Framework.Hosting ## Commit Info Author: Duran Serkan KILIÇ Date: 2026-06-14 21:30:19 +0300 Hash: ebe902574f06c0a2c2c0d8b4b2e28aafbfe418a6 |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.5 | 116 | 6/14/2026 |
| 0.9.5-preview011 | 115 | 6/14/2026 |
| 0.9.5-preview010 | 106 | 6/14/2026 |
| 0.9.5-preview009 | 121 | 6/14/2026 |
| 0.9.5-preview008 | 112 | 6/14/2026 |
| 0.9.5-preview007 | 109 | 6/13/2026 |
| 0.9.5-preview006 | 119 | 6/7/2026 |
| 0.9.5-preview005 | 119 | 6/7/2026 |
| 0.9.5-preview004 | 115 | 6/7/2026 |
| 0.9.5-preview003 | 116 | 6/6/2026 |
| 0.9.5-preview002 | 119 | 6/2/2026 |
| 0.9.5-preview001 | 121 | 5/31/2026 |
| 0.9.4 | 147 | 5/13/2026 |
| 0.9.3 | 138 | 4/25/2026 |
| 0.9.2 | 125 | 4/18/2026 |
| 0.9.1 | 143 | 3/26/2026 |
| 0.9.0 | 128 | 3/25/2026 |
| 0.9.0-preview001 | 133 | 3/22/2026 |
| 0.8.0 | 135 | 3/14/2026 |
| 0.7.0 | 128 | 3/8/2026 |
Not every version includes changes, features or bug fixes. This project can increment version to keep consistency with other DRN.Framework projects.
## Version 0.9.5
### New Features
* **Rate Limiting Settings**: Added validated `DrnAppFeatures:DrnRateLimit` knobs for DRN Hosting rate limiting (`Disabled`, partition log mode, shared token limit, replenishment period, tokens per period, B2B-friendly pre-auth defaults, and optional pre-auth/post-auth overrides), exposed in code as `IAppSettings.Features.RateLimit`.
* **Test Scope Initialization**: `ScopeContext.InitializeForTest(...)` is now public and resets the current async-local scope before initialization, preventing stale test scope data from leaking between helper calls.
* **Stream Hashing Support**: Added stream and file hashing overloads in `HashExtensions` (supporting Blake3, XxHash3, Sha256, Sha512, and keyed Blake3/XxHash3 algorithms) to hash files and large payloads without first materializing them as `BinaryData`.
* **JPEG Payload Validation**: Added public `DRN.Framework.Utils.Validators.JpegValidator` with explicit `JpegValidationResult` and `JpegValidationErrorReason` support for structural, stream-based, and size-bounded JPEG byte validation before persisting uploaded image payloads.
* **Path Security Extensions**: Added `PathExtensions.NormalizeDirectoryPath` (full-path resolution with trailing-separator cleanup) and `IsPathWithinDirectory` (segment-aware containment check using OS-correct path comparison) for safe path validation in file-serving and manifest processing.
* **ScopedLog.CopyFrom**: New method on `IScopedLog` for merging log data, exception, and warning state from one scoped log into another with defensive value cloning for mutable collection types.
* **Configuration Debug View Redaction**: `ConfigurationDebugView` now redacts sensitive configuration values (connection strings, passwords, secrets, tokens, API keys, credentials) by default. A new `GetDebugView(bool includeRawValues)` overload on `IAppSettings` allows opt-in to raw values, but raw inclusion is only permitted in the Development environment.
* **Strict Nexus MAC Key Formats**: `NexusMacKey` now records a `ByteEncoding` `Format` (`Utf8`, `Hex`, `Base64`, or `Base64UrlEncoded`) and accepts only values that resolve directly to exactly 32 bytes. Development auto-generation remains deterministic from `SeedKey` and uses the framework `Hash()` default Base64Url output.
* **SourceKnownEntityId Key-Ring Fallback**: `SourceKnownEntityIdUtils` generates with the default Nexus MAC key and parses with a default-first key ring so IDs generated before key rotation remain parseable while old keys stay configured.
### Bug Fixes
* **Configuration Debug View**: Continues traversing child configuration keys when a higher-priority provider defines a scalar value for the parent section, and renders entries with the value provider's key casing so CI/environment overrides do not hide or rename lower-provider child entries in debug summaries.
* **Prototype Recreation Gate**: `DevelopmentStatus` now enables prototype database recreation only in Development, and honors applied migrations: empty databases can still be recreated for prototyping, while databases with applied migrations require `UsePrototypeModeWhenMigrationExists`.
## Version 0.9.4
Dependencies upgraded to dotnet 10.0.8
## Version 0.9.3
Dependencies upgraded to dotnet 10.0.7
## Version 0.9.2
Dependencies upgraded to dotnet 10.0.6
## Version 0.9.0
My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals and is proud to inherit his spiritual legacy: 'I am not leaving behind any definitive text, any dogma, any frozen, rigid rule as my spiritual legacy. My spiritual wealth is science and reason. Those who wish to embrace me after my death will become my spiritual heirs if they accept the guidance of reason and science on this fundamental axis.'
### Breaking Changes
* **Binary-Incompatible SKEID byte layout** (`SourceKnownEntityIdUtils`): UUID layout migrated to RFC 9562 big-endian; MAC relocated to contiguous bytes 12–15; epoch at byte 0; upper-half MSB sign-toggled for lexicographic sort correctness; lower half split across byte 5 and bytes 9–11.
* **Timestamp precision: seconds → 250ms ticks** (`EpochTimeUtils`, `TimeStampManager`, `SourceKnownIdUtils`): `ConvertToSeconds` renamed to `ConvertToTicks` (250ms units). `TimeStampManager.UtcNow` truncates to nearest 250ms boundary. Epoch-range guard uses `MaxEpochTicks` (2³³ − 1).
* **`ToUnsecure` → `ToPlain`** / **`GenerateUnsecure` → `GeneratePlain`** (`SourceKnownEntityIdUtils`).
* **`NumberBuilder.GetLong` / `NumberParser.Get(long)` residue default**: 31 → 32 bits.
* **Capacity rebalanced** (`SourceKnownIdUtils`): AppId 6→7 bits (max 127), AppInstanceId 5→6 bits (max 63), Sequence 21→18 bits (262,143/tick). `MaxAllowedDriftSeconds` const: 3s → 5s.
> [!WARNING]
> This is a binary-incompatible change. Entity IDs generated with v0.8.0 will not parse correctly in v0.9.0 — IDs must be regenerated. No migration tooling is provided; there are no expected production consumers with persisted v0.8.x entity IDs.
### New Features
* **250ms timestamp precision**: New `TimeStampManager` constants: `PrecisionUnitInMs = 250`, `TicksPerPrecisionUnit = 2,500,000`. Epoch-half constants in `SourceKnownIdUtils`: `TicksPerHalf`, `MaxEpochTicks`. Correct sign-bit logic: first half → negative SKID, second half → positive SKID; monotonic ordering preserved.
* **`NexusAppSettings` constructors**: Added `(byte appId, byte appInstanceId)` overload for programmatic instantiation.
* **Throughput**: ~1,048,576 IDs/s per generator (262,143 × 4 ticks/s); up to ~8.6B IDs/s with 8,192 generators.
## Version 0.8.0
My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals, rooted in his timeless words that 'science is the truest guide in life.' In that spirit, and to honor the 14 March Scientists Day, this release is dedicated to the researchers working for the benefit of humanity, and to the rejection of my first academic paper :) ([JOSS #10176](https://github.com/openjournals/joss-reviews/issues/10176)).
### Breaking Changes
* **SKEID Marker Migration (UUID V4 → V8)**: `SourceKnownEntityIdUtils` markers migrated from `0x4D` (UUID V4) to `0x8D` (UUID V8) for RFC 9562 §5.8 compliance.
> [!WARNING]
> This is a binary-incompatible change. Entity IDs generated with v0.7.0 (`0x4D8D` markers) will not parse correctly in v0.8.0. No migration tooling is provided as there are no production consumers with persisted v0.7.0 entity IDs.
### New Features
* **Clock Drift Detection**: `TimeStampManager` now detects backward clock drift:
* Minor drift (<3 seconds): Cached timestamp is frozen (freeze-and-ride-through strategy). `UtcNowTicks` continues serving the previous higher value until the real clock catches up. No blocking or spin-wait.
* Critical drift (>=3 seconds): `ClockDriftException` is set and `ApplicationLifetime.RequestShutdown()` is called to initiate graceful shutdown. All subsequent `UtcNowTicks` / `UtcNow` calls throw `ClockDriftException`.
* New types: `ClockDriftException`, `ApplicationLifetime`.
## Version 0.7.0
My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals and honors 8 March, International Women's Day, a cause inseparable from his vision of equality. This release is dedicated to freedom of speech, democracy, women's rights, and Prof. Dr. Ümit Özdağ, a defender of Mustafa Kemal Atatürk’s enlightenment ideals.
> [!WARNING]
> Since v0.6.0 (released 10 November 2024), substantial changes have occurred. This release notes file has been reset to reflect the current state of the project as of 08 March 2026. Previous history has been archived to maintain a clean source of truth based on the current codebase.
### New Features
* **Attribute-Based Dependency Injection**
* **Comprehensive Lifetimes**: `[Singleton]`, `[Scoped]`, `[Transient]`, `[HostedService]`, `[Config]`, `[ConfigRoot]`, and Keyed variants (`[SingletonWithKey]`, `[ScopedWithKey]`, `[TransientWithKey]`).
* **Registration**: `AddServicesWithAttributes()` auto-scans assemblies. `ValidateServicesAddedByAttributesAsync()` verifies resolution at startup.
* **Module Pattern**: `HasServiceCollectionModuleAttribute` for custom registration logic.
* **Test Helpers**: `ReplaceInstance`, `ReplaceScoped`, `ReplaceTransient`, `ReplaceSingleton` overrides for integration tests.
* **Configuration System**
* **IAppSettings**: Strong-typed access to config with support for Connection Strings and Sections.
* **Environment Helpers**: `IsDevelopmentEnvironment` and `IsStagingEnvironment` properties for explicit environment checks.
* **[Config] Attribute**: Bind classes directly to config sections (e.g., `[Config("Payment")]`). Support for `[ConfigRoot]`.
* **Layered Sources**: Loads `appsettings`, `appsettings.{Env}`, User Secrets, Env Vars, and **Mounted Settings** (`/appconfig/json-settings/*.json`, `/appconfig/key-per-file-settings`).
* **Environment-Aware Auto-Migration**: `DrnDevelopmentSettings.AutoMigrateDevelopment` (default `true`) and `AutoMigrateStaging` (default `false`) replace the previous single `AutoMigrate` flag, enabling per-environment migration control.
* **Ambient Context & Scoped Cancellation**
* **ScopeContext**: Centralized access to `UserId`, `TraceId`, `Authenticated` status, and ambient `IAppSettings`/`IScopedLog`. Built-in RBAC helpers.
* **ICancellationUtils**: Scoped cancellation management supporting token merging and lifecycle control.
* **Scoped Logging & Diagnostics**
* **IScopedLog**: Request aggregation of actions, properties, and exceptions. `Measure()` for performance tracking and counting.
* **DevelopmentStatus**: Runtime tracking of DB model changes and migration status with environment-aware migration decisions (Development and Staging).
* **Advanced Data & Bit Packing**
* **Bit Packing**: `NumberBuilder` and `NumberParser` (ref structs) for high-performance custom data structures and bit manipulation.
* **Monotonic Pagination**: `IPaginationUtils` for temporal cursor-based pagination leveraging entity IDs.
* **Cryptographic Helpers**: Unified `HashExtensions` (Blake3, XxHash3), `EncodingExtensions` (Base64, Base64Url, Hex), and `SafeApplyMergePatch` (RFC 7386).
* **HTTP & Temporal IDs**
* **HTTP Request Wrappers**: `IInternalRequest`/`IExternalRequest` with standardized Flurl integration, HTTP version policy configuration, and enriched `HttpResponse<T>` diagnostics. Retries/circuit breakers are not configured by this package.
* **Temporal IDs**: `ISourceKnownIdUtils` and `ISourceKnownEntityIdUtils` providing globally sortable identifiers.
* **Secure Entity IDs**: AES-256-ECB single-block encrypted `SourceKnownEntityId` variants with flag-based dispatch via `UseSecureSourceKnownIds` (defaults to `true`).
* `GenerateSecure` / `GenerateUnsecure` explicit methods; `Parse` auto-detects encrypted and plaintext IDs.
* Post-quantum ready — AES-256 retains 128-bit security under Grover's algorithm.
* **Epoch-Based Time Addressing**: `SourceKnownEntityId` byte 5 reserved for epoch indexing, enabling ~34,842 monotonic time years starting from 2025-01-01. Each epoch spans ~136 years (2³¹ seconds × 2 epoch halves). The first epoch requires no configuration.
* **ISourceKnownEntityIdOperations Inheritance**: `ISourceKnownEntityIdUtils` now inherits `ISourceKnownEntityIdOperations` (SharedKernel), formalizing the core contract (`Generate`, `Parse`, `ToSecure`, `ToUnsecure`) for cross-layer use without Utils dependency.
* **Secure ↔ Unsecure Conversion**: `ToSecure` / `ToUnsecure` methods (with nullable overloads) on `SourceKnownEntityIdUtils` for idempotent conversion between encrypted and plaintext `SourceKnownEntityId` forms.
* **Named Constants for GUID Layout**: Replaced magic numbers in `SourceKnownEntityIdUtils` with named constants (`GuidLength`, `MacHashLength`, `MacHashFirstIndex`–`MacHashFourthIndex`) for improved readability and maintainability.
* **Concurrency**
* **Lock-Free Atomics**: `LockUtils` static helpers (`TryClaimLock`, `TryClaimScope`, `ReleaseLock`, `TrySetIfEqual`, `TrySetIfNull`, `TrySetIfNotEqual`, `TrySetIfNotNull`) for lock-free coordination using `Interlocked`. Includes disposable `LockScope` ref struct for automatic lock release via `using`.
* **Core Extensions & Time**
* **Reflection**: Optimized `MethodUtils` with caching, `CreateSubTypes`, and deep discovery (`GetGroupedPropertiesOfSubtype`).
* **Extensions**: Robust set of `string` (Casing, Parsing), `FileInfo` (Efficient line reading), `Stream` (Size guards), and `Dictionary` utilities.
* **High-Perf Time**: `TimeStampManager` (cached UTC seconds) and `RecurringAction` (async-safe timers).
---
Documented with the assistance of [DiSC OS](https://github.com/duranserkan/DRN-Project/blob/develop/.agent/rules/DiSCOS.md)
---
**Semper Progressivus: Always Progressive**
## Commit Info
Author: Duran Serkan KILIÇ
Date: 2026-06-14 21:30:19 +0300
Hash: ebe902574f06c0a2c2c0d8b4b2e28aafbfe418a6