SamSoft.Common
1.4.0
dotnet add package SamSoft.Common --version 1.4.0
NuGet\Install-Package SamSoft.Common -Version 1.4.0
<PackageReference Include="SamSoft.Common" Version="1.4.0" />
<PackageVersion Include="SamSoft.Common" Version="1.4.0" />
<PackageReference Include="SamSoft.Common" />
paket add SamSoft.Common --version 1.4.0
#r "nuget: SamSoft.Common, 1.4.0"
#:package SamSoft.Common@1.4.0
#addin nuget:?package=SamSoft.Common&version=1.4.0
#tool nuget:?package=SamSoft.Common&version=1.4.0
SamSoft.Common
SamSoft.Common is a .NET class library that centralizes reusable building blocks for application code: a Result model with structured errors, an optional Maybe<T> type, and many extension methods for strings, collections, dates, binary data, and cryptography.
| Target framework | .NET 8.0 |
| Language | C# (nullable reference types enabled) |
| License | MIT (see PackageLicenseExpression in SamSoft.Common.csproj) |
| Package readme | This file is packed as the NuGet readme (PackageReadmeFile) |
Table of contents
- Why use this library
- Installation
- Repository layout
- Namespaces and API reference
- Behavior notes and pitfalls
- Security (cryptography)
- Build, test, and package
- Versioning and releases
- Contributing
Why use this library
- Predictable error handling — Model failures with
Result/Errorinstead of scattering try/catch for control flow. - Optional values —
Maybe<T>for presence/absence without overloadingnullsemantics everywhere. - Less boilerplate — Extensions for common string, collection, and date operations used across services and UI layers.
- Single dependency surface — One package instead of copying helpers into each solution.
Installation
Project reference (source / monorepo)
<ItemGroup>
<ProjectReference Include="..\SamSoft.Common\SamSoft.Common.csproj" />
</ItemGroup>
NuGet (when the package is published)
dotnet add package SamSoft.Common
Ensure the consuming project targets .NET 8 or higher (or a compatible TFM that can reference net8.0 libraries).
Repository layout
SamSoft.Common/
├── SamSoft.Common/ # Main library (net8.0)
│ ├── Enums/ # CurrencyCodes, CurrencyCodeExtension
│ ├── ExtensionMethods/ # String, enumerable, date, crypto, etc.
│ ├── OptionObject/ # Maybe<T>, MaybeExtensions
│ ├── Results/ # Result, Result<T>, Error, ResultExtensions
│ └── TokenResponse.cs
├── SamSoft.Common.Tests/ # Unit tests (xUnit.net v3)
├── SamSoft.Common.sln
└── README.md # This file (also packed for NuGet)
Namespaces and API reference
SamSoft.Common.Results
| Type | Purpose |
|---|---|
Result |
Discriminated success/failure without a value. IsSuccess / IsFailure, Error on failure. |
Result<TValue> |
Success carries Value; on failure Value is default. Implicit conversion from T to success. |
Error |
Record: Code, Message, ErrorType, optional Metadata (IReadOnlyDictionary<string, object?>?). Static helpers: Failure, Validation (with optional metadata), NotFound, Conflict, Problem, Create(Exception). Error.None, Error.NullValue. Implicit conversions to string (code) and Result. |
ErrorType |
Failure, Validation, NotFound, Conflict, Problem, None. |
ResultExtensions |
Ensure, Map (sync and Task), Map on Task<Result> → bool, sync/async Bind, Combine, TryCatch, Tap, sync/async Match, FirstFailure. |
Result factories
Result.Success(),Result.Success<T>(T value)Result.Failure(Error),Result.Failure<T>(Error)Result.Create<T>(T? value, Error)/Result.Create<T>(T value)(reference types)Result.FirstFailureOrSuccess(params Result[] results)
Error.Metadata (field validation and extras)
Optional bag for field-level validation messages, correlation IDs, or other structured details. Prefer string keys (property names) and values such as string, string[], or numbers.
var error = Error.Validation(
"User.Validation",
"One or more validation errors occurred.",
new Dictionary<string, object?>
{
["Email"] = new[] { "Email is required.", "Email is invalid." },
["Age"] = new[] { "Age must be at least 18." },
["CorrelationId"] = "abc-123"
});
Result result = error; // implicit conversion to failure Result
// result.Error.Metadata!["Email"] → string[]
Factories that omit metadata leave Metadata as null.
SamSoft.Common.OptionObject
| Type | Purpose |
|---|---|
Maybe<T> |
HasValue / HasNoValue, Value (throws if none), None, From(T?), implicit T → Maybe<T>. GetValueOrDefault(), GetValueOrDefault(T), TryGetValue(out T). Equals / GetHashCode. |
MaybeExtensions |
Bind (async), Match on Task<Maybe<T>>. |
SamSoft.Common (root)
| Type | Purpose |
|---|---|
TokenResponse |
Obsolete — define auth-specific types in your application layer. Primary constructor: token, expiry, isSuperAdmin, refreshToken, tenantId, isAdmin. |
SamSoft.Common.Enums
| Type | Purpose |
|---|---|
CurrencyCodes |
ISO-style numeric currency enum with [Description] metadata; JSON string enum converter. |
CurrencyCodeExtension |
GetCurrencyName(this CurrencyCodes) — resolves description from attributes. |
SamSoft.Common.ExtensionMethods (selected groups)
StringExtension
Null-tolerant helpers where applicable: IsNotNullOrEmpty, EqualWithIgnoreCase (ordinal), NotEqual, IsNumeric, ToCamelCase, IsDate, IsValidEmailAddress, IsValidUrl, IsGuid, ToInt (with optional default), ToDecimal, ToDateTime (multiple overloads), IsDateTime, ToBoolean, IsEmailAddress (stricter), IsValidJson, TryParseJson<T>, ToTitleCase, IsArabic, TryGetInt, FormatWithMask (two overloads), IsDateTime with format → (bool, DateTime), IsNumber.
EnumerableExtensions
IsNullOrNotItem, NotNullOrEmpty, Do, ForEach (three overloads: Action, non-generic IEnumerable, Func projection), RemoveAll on ICollection<T>, Count for non-generic IEnumerable, SetValue, IsNullOrEmpty, NotExists (with/without predicate), ForEachAsync, SelectManyRecursive, IsNotNullOrEmpty, ToObservableCollection, ConvertTo, Each, Cache.
PredicateBuilder
And, Or for combining Expression<Func<T, bool>> with parameter rebinding (useful for dynamic LINQ to Entities predicates).
DateTimeExtensionMethods
TryParseTime, IsValidTime (string), ToEgyptLocalTime (Egypt Standard Time / Africa/Cairo), ToDateByLocalTime (obsolete, alias), GetLocalFromUtc, DateOnly/DateTime conversions, Range between dates, BeginOfMonth / EndOfMonth, BeginOfYear / EndOfYear, WithoutMilliseconds, ConvertToUrlFormat, ToDateFromUrlFormat, EndOfDay, IsBetween, ConvertToSqlFormat, GetDateTimeSqlFormat.
EnumExtensionMethods
GetDescription, ToEnum<TEnum>, ToValues<T> (flags-style enumeration; see pitfalls).
GuidGenerator / GuidVersion
Time-based GUID generation (GenerateTimeBasedGuid overloads), GetVersion, GetDateTimeOffset / GetDateTime / GetLocalDateTime / GetUtcDateTime.
TextEncryptDecrypt
Legacy: SsEncrypt, SsDecrypt (obsolete). Secure: SsEncryptSecure, SsDecryptSecure (password, optional PBKDF2 iterations). See Security.
DeepCopyHelper
DeepCopy<T> via System.Text.Json serialize/deserialize (throws if deserialization yields null).
ExceptionExtension
GetInnerExceptionString, ToFullBlownString, GetMostInner, ExceptionJson.
HashCodeByPropertyExtensions
GetHashCodeOnProperties, GetListHashCode.
ByteArrayExtension / GetFileDataFromFileUrl
GetImageUrl, GetMimeType, FromImageUrl (obsolete — use FileData.CreateFromUrl).
FileData
Record FileData(byte[] File, string MimeContentType); CreateFromUrl with MIME allowlist validation.
Behavior notes and pitfalls
Error.Metadata
- Optional;
nullwhen unused. - Dictionary equality is by reference on the
Errorrecord (contents are not compared deeply). - Prefer serializable value types (
string,string[], numbers) if you map errors to HTTP/JSON responses —object?can deserialize asJsonElement.
Result<T>.Value on failure
On failure, accessing .Value throws InvalidOperationException. Use TryGetValue, GetValueOrDefault, or Match/Bind instead of reading .Value when the outcome is unknown.
Maybe<T> and value types
HasNoValue is implemented as _value is null. For reference types, null means “none.” For unconstrained value types (e.g. Maybe<int>), there is no distinct “none” separate from default(T); None and 0 are not distinguishable the same way as for references. Prefer Maybe<T?> (e.g. Maybe<int?>) or reference types when you need a clear empty state.
EnumerableExtensions.ForEach and lambdas
There are two ForEach overloads: one takes Action<T>, another Func<T, TResult>. A lambda like x => n += x can bind to the Func overload (because += yields a value). Use a block x => { n += x; } when you intend side effects only.
EnumExtensionMethods.ToValues<T>
Validation (T must be an enum type) runs when the iterator is executed (e.g. foreach, ToList()), not merely when ToValues() is called.
JSON deep copy
DeepCopy<T> depends on System.Text.Json semantics. Polymorphic graphs, cycles, or types that do not round-trip cleanly may fail or throw.
DateTimeExtensionMethods.ToEgyptLocalTime
Uses Egypt Standard Time (Windows) or Africa/Cairo (IANA). The obsolete ToDateByLocalTime is an alias. Availability depends on the OS time zone database.
EnumerableExtensions.ForEachAsync
Pass maxDegreeOfParallelism to limit concurrent tasks. The default runs all tasks in parallel via Task.WhenAll.
Security (cryptography)
| API | Use case |
|---|---|
SsEncrypt / SsDecrypt |
Legacy compatibility only for data encrypted by older versions. Weak by modern standards (fixed IV, MD5-based key material). Do not use for new secrets. |
SsEncryptSecure / SsDecryptSecure |
Preferred for new data: random salt and IV per message, PBKDF2 (SHA-256), AES-CBC. Pass passwords or key material from configuration, vaults, or user input—never embed secrets in source code or commit them to the repository. |
Rotate keys and migration strategies if you move from legacy to secure formats.
Build, test, and package
# Restore and compile
dotnet build SamSoft.Common.sln
# Run tests (xUnit.net v3)
dotnet test SamSoft.Common.sln
# Optional: create a NuGet package (produces .nupkg and .snupkg in ./artifacts)
dotnet pack SamSoft.Common/SamSoft.Common.csproj -c Release -o ./artifacts
Publish to NuGet.org (or another feed)
dotnet pack creates two files:
| File | Purpose |
|---|---|
SamSoft.Common.1.4.0.nupkg |
Main package — upload this first |
SamSoft.Common.1.4.0.snupkg |
Symbol package — upload after the main package exists on the feed |
If you upload the .snupkg first (or only upload symbols), NuGet returns:
A package with ID 'SamSoft.Common' and version '1.4.0' does not exist. Please upload the package before uploading its symbols.
Correct order (CLI):
dotnet pack SamSoft.Common/SamSoft.Common.csproj -c Release -o ./artifacts
# 1) Main package first
dotnet nuget push ./artifacts/SamSoft.Common.*.nupkg \
--source https://api.nuget.org/v3/index.json \
--api-key YOUR_NUGET_API_KEY \
--skip-duplicate
# 2) Symbols second (only the .snupkg — do not use *.nupkg here)
dotnet nuget push ./artifacts/SamSoft.Common.*.snupkg \
--source https://api.nuget.org/v3/index.json \
--api-key YOUR_NUGET_API_KEY \
--skip-duplicate
On the nuget.org web UI, upload the .nupkg first, wait until it appears on the site, then upload the .snupkg.
If you do not need separate symbol packages, set <IncludeSymbols>false</IncludeSymbols> in SamSoft.Common.csproj and only publish the .nupkg.
The test project (SamSoft.Common.Tests) targets .NET 8 and uses xUnit.net v3 with broad coverage of public APIs and regression scenarios.
Versioning and releases
- Assembly and NuGet version are driven by
SamSoft.Common.csproj(AssemblyVersion,Version,FileVersion). - Bump the version when you publish a new package or introduce breaking API changes (document them for consumers).
- License acceptance:
PackageRequireLicenseAcceptanceis set in the project file; consumers may need to acknowledge the MIT license in internal feeds.
Contributing
- Keep changes focused and consistent with existing naming and patterns.
- Add or update unit tests in
SamSoft.Common.Testsfor any new or changed behavior. - Update this README when you add public API surface that users depend on.
- Do not commit secrets, credentials, or production connection strings.
Additional resources
- Result pattern (background): the
ErrorTypeenum file references an external talk link in source; seeErrorType.csin the repository.
For questions or improvements, use your team’s usual code review and issue workflow.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on SamSoft.Common:
| Package | Downloads |
|---|---|
|
SamSoft.Mediator.CQRS
Result-typed CQRS mediator for .NET with pipeline behaviors, FluentValidation, and DI integration. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 1.4.0 | 259 | 8/10/2026 | |
| 1.3.0 | 118 | 8/10/2026 | |
| 1.2.2 | 214 | 4/19/2026 | |
| 1.2.1 | 126 | 4/19/2026 | |
| 1.2.0 | 127 | 4/19/2026 | |
| 1.1.0 | 135 | 4/19/2026 | |
| 1.0.8 | 250 | 6/20/2025 | |
| 1.0.7 | 347 | 6/10/2025 | |
| 1.0.6 | 351 | 6/10/2025 | |
| 1.0.5 | 342 | 6/10/2025 | |
| 1.0.4 | 353 | 6/10/2025 | |
| 1.0.3 | 325 | 6/10/2025 | |
| 1.0.2 | 186 | 5/30/2025 | |
| 1.0.1 | 240 | 5/22/2025 | |
| 1.0.0 | 201 | 5/22/2025 |