The .NET extension method library that kills boilerplate. Type conversions, string manipulation, collection utilities, date helpers, HTML sanitization, batch processing, diagnostics, resilience — all as clean one-liners on the types you already use.
Built by opentail.net — Dmitri Rechetilov
Every .NET project ends up writing the same utility code. Safe type conversion with fallbacks. String truncation that doesn't break HTML entities. Date arithmetic that handles edge cases. Retry loops with backoff. Pluralization. CSV export. DataRow extraction that doesn't throw on DBNull.
This library is that code — battle-tested, documented, and shipped as a single using Transformations; import.
Pick what you need:
| Package | What you get | Dependencies |
|---|---|---|
Transformations |
Everything — the all-in-one package | ASP.NET Core, SqlClient, Configuration |
Transformations.Core |
Strings, collections, conversion, dates, files, diagnostics, batching, resilience | SqlClient only |
Transformations.Data |
DataRow, DataReader, SQL parameters, CSV, DataTable conversion | Core |
Transformations.Web |
HTTP, session, cookies, query strings, configuration, SelectList helpers | Core + ASP.NET Core |
Transformations.Dapper |
Resilient Dapper queries with transient SQL fault detection | Core + Dapper + SqlClient |
Transformations.EntityFramework |
Resilient SaveChanges, ChangeTracker audit logging, IQueryable CSV export | Core + EF Core |
Transformations.Mapping |
Zero-reflection compile-time object mapper with automatic type conversion | Core + Source Generator |
Core has zero ASP.NET Core dependency. If you're building a console app, background service, or library — use Core and keep your dependency graph lean.
<!-- All-in-one -->
<PackageReference Include="Transformations" Version="2.0.0" />
<!-- Or pick your slice -->
<PackageReference Include="Transformations.Core" Version="2.0.0" />
<PackageReference Include="Transformations.Data" Version="2.0.0" />
<PackageReference Include="Transformations.Web" Version="2.0.0" />
<PackageReference Include="Transformations.Dapper" Version="2.0.0" />
<PackageReference Include="Transformations.EntityFramework" Version="2.0.0" />
<PackageReference Include="Transformations.Mapping" Version="2.0.0" />using Transformations;
// Type conversion — safe, with fallbacks
string input = "42";
int value = input.ConvertTo<int>(); // 42
int safe = "nope".ConvertTo<int>(-1); // -1
// String helpers
"Hello World".GetInitials(); // "HW"
"test".Repeat(3); // "testtesttest"
"<b>Hello</b>".StripHtml(); // "Hello"
// Pluralization (regex-based, handles irregulars)
"child".Pluralize(3); // "children"
"person".Pluralize(5); // "people"
"Box".Pluralize(2); // "Boxes"
"item".Pluralize(1); // "item"
// Range checks
5.IsBetween(1, 10); // true
// Enum utilities
MyEnum.Value.GetEnumDescription2(); // reads [Description] attribute
"Active".ToEnum<Status>(); // parses string to enum
// Date helpers
DateTime.Now.IsWeekend(); // true/false
2024.GetEasterSunday(); // Easter date calculation
// Semantic compare
"+1 (555) 111-2222".IsSemanticMatch("15551112222", SemanticType.PhoneNumber); // true
// Diagnostics (chainable trace — zero-cost when disabled)
var userId = 42.Trace("resolved user id");
// Resilience (sync retry with exponential backoff)
int result = Resilience.Retry(() => int.Parse("42"), retryCount: 3,
initialDelay: TimeSpan.FromMilliseconds(50));| Class | Purpose | Key Methods |
|---|---|---|
| BasicTypeConverter | Safe type conversion with defaults | ConvertTo<T>, TryConvertTo<T>, ToInt, ToDouble, ToDateTime, ToChar, ToGuid |
| BitConvertor | Byte array ↔ primitive conversion | ConvertBitsToInt, ConvertBitsToLong, GetBytes, TryConvertBitsTo* |
| CollectionConvertor | Bulk collection conversion | IEnumerable, DataTable, and array conversion utilities |
Every numeric type, bool, char, string, DateTime, Guid — covered with both ConvertTo<T> and TryConvertTo<T> overloads, plus nullable variants with explicit defaults.
| Class | Purpose | Key Methods |
|---|---|---|
| StringHelper | Core string operations | StripHtml, GetInitials, Repeat, WordCount, Truncate, SplitCamelCase, ParseConnectionString |
| AdditionalStringHelper | HTML, encoding, sanitization | SanitizeHtml, TruncateSemantic, HtmlEncode/HtmlDecode, UrlEncode/UrlDecode, ToBase64String/FromBase64String, ToPlural |
| StringPluralizationExtensions | Regex-based English pluralization | Pluralize — handles irregulars (child→children, person→people, ox→oxen, tooth→teeth, goose→geese, mouse→mice) and suffix rules |
| RegExHelper | Regex wrappers | IsMatch, Match, Split |
| TransformationsHelper | Encoding utilities | Base64/Hex encode/decode |
string safe = rawHtml.SanitizeHtml(HtmlSanitizationPolicy.PermitLinks);
string preview = rawHtml.TruncateSemantic(140, countHtmlTags: false);Three policies: StripAll, PermitInlineFormatting (b/i/u/em/strong), PermitLinks (+ safe anchor tags). Script blocks, on* event attributes, and javascript: hrefs are always stripped.
See Sanitation.md for the technical deep dive.
| Class | Purpose | Key Methods |
|---|---|---|
| DateHelper | Date arithmetic and queries | AddSafely, CalculateAge, DateDiff, IsToday, IsTomorrow, IsLeapYear, FirstOfMonth, LastOfMonth, FirstDateOfTheWeek, SetTime |
| TimeSpanHelper | Readable time formatting | ToReadableTimeString (6 output formats), TimeSinceDate, ToTimeSpanAs |
| StopwatchHelper | Stopwatch result extraction | ElapsedSeconds, ElapsedMinutes, ElapsedHours, ElapsedDays, ToElapsedTimeString |
| HolidayHelper | UK & US public holidays | GetEasterSunday, GetGoodFriday, GetEnglishBankHolidays, GetEnglishWorkingDaysCount, GetThanksgivingDay, GetMemorialDay, GetPresidentsDay |
TimeSpanHelper output formats:
| Format | Example (2h 30m 15s) |
|---|---|
hhmm |
02:30 |
hhmmss |
02:30:15 |
ShorthandTotalTime |
02h |
ShorthandTime |
02h:30m:15s:000ms |
VerboseTotalTime |
2 hours |
VerboseTime |
2 hours 30 minutes 15 seconds 0ms |
| Class | Purpose | Key Methods |
|---|---|---|
| ArrayHelper | High-performance array operations | BlockCopy, ClearAll, CombineArrays, PrependItem |
| CollectionHelper | List/collection utilities | IsNullOrEmpty, HasItems, AddUnique, AddRangeUnique, CloneList, ContainsIgnoreCase |
| CsvHelper | CSV generation | ToCsv (from IEnumerable or DataTable, custom separators, text qualifiers) |
// Convert a mixed array with a fallback for failures
var converted = BatchTransformations.BatchConvert(
new object?[] { "1", "bad", 3 }, defaultValue: -1).ToList();
// In-place HTML stripping on a span — zero allocation
string?[] lines = { "<b>alpha</b>", "<i>beta</i>" };
BatchTransformations.BatchTransformInPlace(lines.AsSpan(),
BatchTransformations.BatchStringTransformation.StripHtml);See Batching.md for the performance deep dive.
| Class | Purpose | Key Methods |
|---|---|---|
| FileInfoHelper | File operations | Rename, RenameFileWithoutExtension, ChangeExtension, SetAttributes, CopyTo, Delete (batch) |
| DirectoryInfoHelper | Directory operations | CopyTo (recursive), FindFileRecursive (by pattern or predicate), GetFiles (multiple patterns) |
| StreamHelper | Stream utilities | CopyTo, CopyToMemory, ReadAllBytes, ReadToEnd, GetReader, GetWriter, SeekToBegin |
| StreamExtensions | Stream processing for large files | ForEachLine, CopyToWithProgress |
| Class | Purpose | Key Methods |
|---|---|---|
| Resilience | Synchronous + async retry with backoff, jitter, and retry callbacks | Retry(...), RetryAsync(...) — filter by exception type, fail-fast, optional jitter, onRetry/onRetryAsync metadata callbacks |
| DiagnosticsProbe | Process metrics | GetProcessMetrics — CPU, memory, threads, and optional GPU/VRAM probing |
| DiagnosticExtensions | Chainable tracing | Trace<T> + global IsTraceEnabled toggle — null-safe, zero-cost when off |
| Class | Purpose | Key Methods |
|---|---|---|
| SemanticStringComparer | Intent-based string matching | IsSemanticMatch(..., SemanticType) — phone numbers, emails, and more |
| ObjectDelta | Shallow object change detection | Compare<T>(...) with [SkipDelta] attribute for opt-out properties |
| WebAndFileExtensions | URL/path normalization | AppendUrlSegment, ToLocalPath |
| MeasurementExtensions | Human-readable formatting | ToSizeString (bytes → KB/MB/GB), ToShortElapsedString |
| Class | Purpose | Key Methods |
|---|---|---|
| DataRowConverter | Safe DataRow value extraction | GetValue<T>, TryGetValue<T>, GetStringValue, HasRows, HasColumns, IsNumericValue |
| DataReaderHelper | IDataReader extensions | Type-safe reader column extraction |
| SqlHelper | SqlParameter construction | ToSqlParameter, ToSqlParameterList, SetSqlDbType, SetValue, SetDirection |
| Class | Purpose | Key Methods |
|---|---|---|
| DapperResilienceExtensions | Retry-wrapped Dapper queries | QueryWithRetryAsync<T>, QuerySingleOrDefaultWithRetryAsync<T>, ExecuteWithRetryAsync, ExecuteScalarWithRetryAsync<T> |
| SqlTransientFaultDetector | Transient SQL fault classification | IsTransient — deadlocks, timeouts, Azure throttling, transport errors |
| SqlParameterBridge | Anonymous object → SqlParameter[] | ToSqlParameters — with optional SqlDbType mappings |
// Retry-wrapped query — 3 retries with 200ms exponential backoff
var users = await connection.QueryWithRetryAsync<User>(
"SELECT * FROM Users WHERE Active = @Active",
new { Active = true });
// Check if a caught exception is transient
catch (SqlException ex) when (SqlTransientFaultDetector.IsTransient(ex))
{
logger.LogWarning(ex, "Transient SQL fault, will retry");
}| Class | Purpose | Key Methods |
|---|---|---|
| DbContextResilienceExtensions | Retry-wrapped SaveChanges | SaveChangesWithRetryAsync — exponential backoff, exception filters, CancellationToken |
| ChangeTrackerAuditExtensions | Structured audit from ChangeTracker | GetAuditEntries — captures Added/Modified/Deleted property changes with key values and timestamps |
| QueryableCsvExtensions | IQueryable → CSV export | ToCsvAsync<T> — materializes and joins with configurable separator |
// Resilient save with retry
int saved = await context.SaveChangesWithRetryAsync();
// Capture audit entries before save
var audit = context.GetAuditEntries(EntityState.Modified);
await context.SaveChangesWithRetryAsync();
foreach (var entry in audit)
logger.LogInformation("{Entity}.{Prop}: {Old} → {New}",
entry.EntityType, entry.PropertyName, entry.OriginalValue, entry.CurrentValue);
// Export query results to CSV
string csv = await context.Users.Select(u => u.Name).ToCsvAsync();| Attribute | Purpose |
|---|---|
[MapTo<TTarget>] |
Marks a partial class for compile-time mapping — generates To{Target}() and From{Target}() |
[IgnoreMap] |
Excludes a property from mapping |
[MapProperty("Name")] |
Maps a source property to a differently-named target property |
Zero reflection. NativeAOT safe. Compile-time validated. Type mismatches are resolved automatically via ConvertTo<T>.
Generated mapper diagnostics:
TXMAP001warns when a target member is not mapped from source.- Opt-in error mode:
<PropertyGroup>
<TransformationsMappingUnmappedMembersAsErrors>true</TransformationsMappingUnmappedMembersAsErrors>
</PropertyGroup>[MapTo<UserDto>]
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; } // auto-converted to string via .ToString()
[IgnoreMap]
public string PasswordHash { get; set; }
}
// Generated at compile time — no reflection, no runtime cost:
UserDto dto = user.ToUserDto();
User back = User.FromUserDto(dto);| Class | Purpose | Key Methods |
|---|---|---|
| ConfigurationHelper | IConfiguration extensions |
GetSetting, GetValue<T>, GetConnectionString, TryGetSetting, ContainsKey |
| QueryStringHelper | Query string parsing | GetAllQueryStrings, TryGetQuery<T>, HasValue, ParseQueryString |
| SessionHelper | Session state | GetValue<T>, SetValue<T>, Exists, GetAllStrings, Clear, Remove |
| CookieHelper | Cookie management | Get, Set, Delete via ICookieHelper |
| WebHelper | HTTP response utilities | Redirect, Reload, SetFileNotFound, SetInternalServerError, SetStatus |
| ResponseHelper | Response extensions | RedirectAsync with window target support |
| SelectListExtensions | DataTable → dropdown | ToSelectList — converts a DataTable to List<SelectListItem> for MVC/Razor dropdowns |
dotnet build
dotnet testPrerequisites: .NET 8, 9, or 10 SDK
├── Transformations/ # Monolith (all-in-one package)
│ ├── BasicTypeConverter.cs # Safe generic type conversion
│ ├── StringHelper.cs # Core string extensions
│ ├── AdditionalStringHelper.cs # HTML sanitization, pluralization, encoding
│ ├── CollectionConvertor.cs # Bulk collection conversion
│ ├── CollectionHelper.cs # List/collection utilities
│ ├── BatchTransformations.cs # Low-allocation batch conversion/transform
│ ├── DateHelper.cs # Date arithmetic, IsToday, CalculateAge
│ ├── TimeSpanHelper.cs # Readable time string formatting
│ ├── StopwatchHelper.cs # Stopwatch elapsed extraction
│ ├── HolidayHelper.cs # UK & US public holiday calculations
│ ├── ArrayHelper.cs # BlockCopy, CombineArrays, PrependItem
│ ├── BitConvertor.cs # Byte/primitive round-trip conversion
│ ├── FileInfoHelper.cs # File rename, copy, delete
│ ├── DirectoryInfoHelper.cs # Directory copy, recursive file search
│ ├── StreamHelper.cs # Stream read/write/copy
│ ├── StreamExtensions.cs # Line iteration and copy progress
│ ├── CsvHelper.cs # CSV generation from collections/DataTable
│ ├── DataRowConverter.cs # Safe DataRow value access
│ ├── DataReaderHelper.cs # IDataReader extensions
│ ├── SqlHelper.cs # SqlParameter builder
│ ├── ConfigurationHelper.cs # IConfiguration extensions
│ ├── QueryStringHelper.cs # Query string utilities
│ ├── SessionHelper.cs # Session state extensions
│ ├── CookieHelper.cs # Cookie management
│ ├── WebHelper.cs # HTTP redirect/status helpers
│ ├── ResponseHelper.cs # Response extensions
│ ├── Inspect.cs # Type validation (IsNumeric, IsDate, etc.)
│ ├── Helper.cs # IsNull, IsNotNull, ComputeHash
│ ├── DiagnosticsProbe.cs # Process + lightweight GPU/VRAM metrics
│ ├── DiagnosticExtensions.cs # Chainable trace helper
│ ├── Resilience.cs # Sync retry with exponential backoff
│ ├── SemanticStringComparer.cs # Intent-based string comparisons
│ ├── ObjectDelta.cs # Shallow object diff + SkipDelta
│ ├── MeasurementExtensions.cs # Size/time compact formatting
│ ├── WebAndFileExtensions.cs # URL segment + local path helpers
│ ├── EnumHelper.cs # Enum description/parse extensions
│ ├── ComparableHelper.cs # Range/between checks
│ ├── ExtensionHelper.cs # Clamped index helpers
│ ├── RegExHelper.cs # Regex match/split wrappers
│ ├── MiscHelper.cs # Miscellaneous utilities
│ └── Transform.cs # Shared enumerations
│
├── Transformations.Core/ # Core-only package (no ASP.NET Core)
├── Transformations.Data/ # Data package (Core + DataRow/SQL)
├── Transformations.Web/ # Web package (Core + ASP.NET Core)
│ └── SelectListExtensions.cs # DataTable → SelectListItem
│
├── Transformations.Dapper/ # Dapper package (Core + Dapper + SqlClient)
│ ├── DapperResilienceExtensions.cs # Retry-wrapped Query/Execute
│ ├── SqlTransientFaultDetector.cs # Transient SQL error classification
│ └── SqlParameterBridge.cs # Anonymous object → SqlParameter[]
│
├── Transformations.EntityFramework/ # EF Core package (Core + EF Core)
│ ├── DbContextResilienceExtensions.cs # SaveChangesWithRetryAsync
│ ├── ChangeTrackerAuditExtensions.cs # ChangeTracker → AuditEntry[]
│ └── QueryableCsvExtensions.cs # IQueryable → CSV
│
├── Transformations.Mapping/ # Zero-reflection mapper (Core + source generator)
│ └── Attributes.cs # [MapTo<T>], [IgnoreMap], [MapProperty]
│
├── Transformations.Mapping.Generator/ # Incremental source generator (netstandard2.0)
│ └── MappingGenerator.cs # Emits To{Target}() and From{Target}() methods
│
├── Transformations.Analyzers/ # Roslyn analyzer (netstandard2.0)
│ ├── DeprecatedApiAnalyzer.cs # TX0001 diagnostic
│ └── DeprecatedApiCodeFixProvider.cs # Lightbulb rename fix
│
├── Transformations.Tests/ # NUnit test project (90+ test files)
├── Transformations.Dapper.Tests/ # Dapper package tests
├── Transformations.EntityFramework.Tests/ # EF Core package tests
├── Transformations.Mapping.Tests/ # Mapper tests (verifies generated code)
├── Transformations.Analyzers.Tests/ # Analyzer tests
│
├── COOKBOOK.md # Problem-first recipes
├── Batching.md # Performance deep dive
├── Sanitation.md # HTML sanitization deep dive
├── DEPRECATION_POLICY.md # Versioned API deprecation plan
└── README.md
Legacy APIs are retained with [Obsolete] markers and explicit replacements:
| Legacy | Replacement | Why |
|---|---|---|
ExtensionHelper.Between<T>(...) |
BetweenExclusive(...) |
Explicit half-open range semantics |
ComparableHelper.IsBetween<T>(...) |
IsBetweenInclusive(...) |
Inclusive behavior is now clear from the name |
EnumHelper.GetEnumDescription2(...) |
GetEnumDescription(...) |
Numbered suffixes removed |
Deprecation timeline:
| Version | Behavior |
|---|---|
| 2.0.0 | [Obsolete] warning-only with replacement guidance |
| 2.1.0 | Set DeprecationErrorLevel=true to promote to build errors |
| 2.2.0 | Deprecated APIs removed |
See DEPRECATION_POLICY.md for full details.
| Package | Version | Used By |
|---|---|---|
Microsoft.AspNetCore.App (FrameworkReference) |
— | Transformations, Transformations.Web |
Microsoft.Data.SqlClient |
7.0.0 | SqlHelper, DataReaderHelper, StringHelper.ParseConnectionString |
Microsoft.Extensions.Configuration |
10.0.5 | ConfigurationHelper |
Microsoft.Extensions.Configuration.Binder |
10.0.5 | ConfigurationHelper.GetValue<T> |
Dapper |
2.1.72 | Transformations.Dapper |
Microsoft.EntityFrameworkCore |
9.0.7 | Transformations.EntityFramework |
Transformations.Core and Transformations.Data have no ASP.NET Core dependency.
GitHub Actions workflow: .github/workflows/ci-quality-gates.yml
- Build + test on .NET 10
- Line/branch coverage thresholds from Cobertura report
- Public-method test policy for critical modules (
BasicTypeConverter,CollectionConvertor,HolidayHelper) - XML documentation artifact publication
- COOKBOOK.md — Problem-first recipes: "How do I safely convert untrusted input?", "How do I strip scripts from HTML?", etc.
- Batching.md — Performance deep dive on
ArrayHelperandBatchTransformations - Sanitation.md — Technical deep dive on the HTML sanitization policy engine
- DEPRECATION_POLICY.md — Versioned API deprecation timeline
MIT — Copyright © 2025 opentail.net