Moller.Utilities 1.2.0

dotnet add package Moller.Utilities --version 1.2.0
                    
NuGet\Install-Package Moller.Utilities -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Moller.Utilities" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Moller.Utilities" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Moller.Utilities" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Moller.Utilities --version 1.2.0
                    
#r "nuget: Moller.Utilities, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Moller.Utilities@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Moller.Utilities&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Moller.Utilities&version=1.2.0
                    
Install as a Cake Tool

Moller.Utilities

Moller.Utilities is a lightweight .NET utility library for production-friendly helpers that come up often in real applications.

Current feature areas:

  • Guard and validation helpers
  • Result types
  • Execution helpers for repeated work, retries, and timeouts
  • Disposable action wrappers
  • Collection helpers
  • String helpers

Target Framework

This library currently targets net10.0.

Guards

Use Validation for fluent argument and state checks.

using Moller.Utilities.Guards;

string name = "Alice";

var validatedName = Validation.For(name)
    .IsNotNull()
    .IsNotEmpty()
    .IsNotNullOrWhiteSpace()
    .Ensure(x => x!.Length <= 50, "Name must be 50 characters or fewer.")
    .Value();

Instance-style syntax is still available:

using Moller.Utilities.Guards;

string connectionName = "Primary";

var value = connectionName
    .Validate()
    .IsNotNull()
    .IsNotEmpty()
    .Value();

Results

Use Result and Result<T> when failure is an expected outcome and exceptions would be too heavy.

using Moller.Utilities.Results;

Result<int> ParsePort(string value)
{
    return int.TryParse(value, out var port) && port > 0
        ? Result<int>.Success(port)
        : Result<int>.Failure("The port must be a positive integer.");
}

var portResult = ParsePort("8080");
var timeoutSeconds = portResult.Map(port => port / 1000);

Execution

PeriodicActionRunner

PeriodicActionRunner runs an Action immediately, then repeats it on a fixed interval using a background task.

using Moller.Utilities.Execution;

using var runner = new PeriodicActionRunner();

runner.Start(() =>
{
    Console.WriteLine($"Heartbeat: {DateTimeOffset.Now}");
}, intervalMilliseconds: 1000);

await Task.Delay(5000);
await runner.StopAsync();

RetryPolicy

Use RetryPolicyBuilder to retry transient failures. Logging is built in when you provide an ILogger.

using Microsoft.Extensions.Logging;
using Moller.Utilities.Execution;

RetryPolicy retryPolicy = new RetryPolicyBuilder()
    .WithMaxAttempts(3)
    .WithExponentialBackoff(TimeSpan.FromMilliseconds(100))
    .WithLogger(loggerFactory.CreateLogger("RetryPolicy"))
    .Build();

await retryPolicy.ExecuteAsync(async cancellationToken =>
{
    await CallRemoteServiceAsync(cancellationToken);
});

TimeoutPolicy

Use TimeoutPolicyBuilder to fail fast when work takes too long.

using Microsoft.Extensions.Logging;
using Moller.Utilities.Execution;

TimeoutPolicy timeoutPolicy = new TimeoutPolicyBuilder()
    .After(TimeSpan.FromSeconds(2))
    .WithLogger(loggerFactory.CreateLogger("TimeoutPolicy"))
    .Build();

await timeoutPolicy.ExecuteAsync(async cancellationToken =>
{
    await DoWorkAsync(cancellationToken);
});

Disposables

DisposableAction and AsyncDisposableAction make cleanup scopes easy to express.

using Moller.Utilities.Disposables;

using var cleanup = new DisposableAction(() => Console.WriteLine("Cleaning up."));
await using var asyncCleanup = new AsyncDisposableAction(() => ValueTask.CompletedTask);

Collections

The collection extensions focus on high-value helpers instead of trying to replace LINQ.

using Moller.Utilities.Collections;

var batches = Enumerable.Range(1, 10).Batch(3);

var cache = new Dictionary<string, int>();
var answer = cache.GetValueOrAdd("answer", _ => 42);

await Enumerable.Range(1, 5).ForEachAsync(
    async (value, cancellationToken) =>
    {
        await Task.Delay(10, cancellationToken);
        Console.WriteLine(value);
    },
    maxDegreeOfParallelism: 2);

Text

The text helpers cover a small set of common string operations.

using Moller.Utilities.Text;

string? normalized = "  hello  ".NullIfWhiteSpace();
string preview = "This is a longer sentence".Truncate(12);
string masked = "1234567890".Mask(unmaskedPrefix: 2, unmaskedSuffix: 2);
string csv = ", ".JoinNonEmpty("alpha", null, " beta ");
string slug = "Crème brûlée".ToSlug();

Logging

Logging is intentionally built into the operational helpers only:

  • PeriodicActionRunner
  • RetryPolicy
  • TimeoutPolicy

Pure value helpers like results, collections, text helpers, and disposable wrappers do not log internally.

Each accepts an optional ILogger (or ILogger<T>) via its constructor or builder. If no logger is provided, a NullLogger is used and no output is produced.

Serilog Example

using Moller.Utilities.Execution;
using Microsoft.Extensions.Logging;
using Serilog;

// dotnet add package Serilog
// dotnet add package Serilog.Sinks.File
// dotnet add package Serilog.Extensions.Logging

var serilogLogger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.File(
        path: "logs\\myapp-.log",
        rollingInterval: RollingInterval.Day,
        outputTemplate:
            "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

using ILoggerFactory loggerFactory =
    new LoggerFactory().AddSerilog(serilogLogger, dispose: true);

ILogger<PeriodicActionRunner> logger =
    loggerFactory.CreateLogger<PeriodicActionRunner>();

var runner = new PeriodicActionRunner(logger);

Notes

  • PeriodicActionRunner.Start(...) throws if the runner is already active.
  • StopAsync() is safe to call even if the runner was never started.
  • RetryPolicy retries all exceptions by default unless you narrow handled exception types.
  • TimeoutPolicy throws TimeoutException when the timeout window is exceeded.
  • Validation.IsInRange(...) and the ordering helpers support comparable types broadly, including strings and common BCL types like DateTime, DateOnly, TimeSpan, and Version.
  • Validation.IsPositive(), IsNegative(), and IsNotZero() remain numeric-only.
  • The package introduces no new external dependencies beyond Microsoft.Extensions.Logging.

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 0 3/23/2026
1.1.0 0 3/18/2026
1.0.3 0 3/18/2026
1.0.2 0 3/18/2026
1.0.1 0 3/18/2026
1.0.0 0 3/17/2026