Myth.Commons 4.0.1-preview.2

This is a prerelease version of Myth.Commons.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Myth.Commons --version 4.0.1-preview.2
                    
NuGet\Install-Package Myth.Commons -Version 4.0.1-preview.2
                    
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="Myth.Commons" Version="4.0.1-preview.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Myth.Commons" Version="4.0.1-preview.2" />
                    
Directory.Packages.props
<PackageReference Include="Myth.Commons" />
                    
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 Myth.Commons --version 4.0.1-preview.2
                    
#r "nuget: Myth.Commons, 4.0.1-preview.2"
                    
#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 Myth.Commons@4.0.1-preview.2
                    
#: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=Myth.Commons&version=4.0.1-preview.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Myth.Commons&version=4.0.1-preview.2&prerelease
                    
Install as a Cake Tool

<img style="float: right;" src="myth-commons-logo.jpg" alt="drawing" width="250"/>

Myth.Commons

NuGet Version NuGet Version

License

pt-br en

Myth.Commons is a foundational .NET library providing essential utilities and patterns for building robust, maintainable enterprise applications. It offers JSON serialization, string manipulation, DDD building blocks, and centralized service provider management for cross-library dependency resolution.

Features

  • JSON Extensions: Flexible serialization/deserialization with configurable settings
  • String Utilities: Rich set of string manipulation methods
  • URL Extensions: URL encoding helpers
  • Value Objects: DDD base class for implementing value objects with structural equality
  • Constants: Type-safe constants using SmartEnum pattern
  • Service Provider Management: Global service provider for cross-library dependency resolution
  • Scoped Services: Pattern for executing operations within automatic service scopes
  • Pagination Support: Value objects and interfaces for paginated results
  • Collection Extensions: Helper methods for working with enumerables

Installation

dotnet add package Myth.Commons

Table of Contents

JSON Extensions

Powerful JSON serialization and deserialization with System.Text.Json, featuring global configuration, custom converters, and flexible naming strategies.

Basic Usage

using Myth.Extensions;

// Serialize to JSON
var user = new User { Id = 1, Name = "John Doe", Email = "john@example.com" };
var json = user.ToJson();
// {"id":1,"name":"John Doe","email":"john@example.com"}

// Deserialize from JSON
var userObj = json.FromJson<User>();

Global Configuration

Configure JSON settings globally for your entire application:

JsonExtensions.Configure( settings => settings
    .UseCaseStrategy( CaseStrategy.SnakeCase )
    .IgnoreNull()
    .Minify()
);

var json = user.ToJson();
// {"id":1,"name":"John Doe","email":"john@example.com"}

Per-Operation Configuration

Override global settings for specific operations:

// Use snake_case for this operation only
var json = user.ToJson( settings => settings
    .UseCaseStrategy( CaseStrategy.SnakeCase )
);
// {"id":1,"name":"john doe","email":"john@example.com"}

// Minify JSON output
var compactJson = user.ToJson( settings => settings.Minify() );

// Ignore null values
var jsonWithoutNulls = user.ToJson( settings => settings.IgnoreNull() );

Interface to Concrete Type Converters

Handle interfaces and abstract types during serialization/deserialization:

// Using generic converter
var json = user.ToJson( settings => settings
    .UseInterfaceConverter<IAddress, Address>()
);

// Using non-generic converter
var json = user.ToJson( settings => settings
    .UseInterfaceConverter( typeof( IAddress ), typeof( Address ) )
);

Custom JSON Converters

Add custom System.Text.Json converters:

var json = user.ToJson( settings => settings
    .UseCustomConverter( new CustomDateTimeConverter() )
);

Advanced JSON Settings

Access underlying JsonSerializerOptions for fine-grained control:

var json = user.ToJson( settings => {
    settings.IgnoreNull().Minify();
    settings.OtherSettings = options => {
        options.MaxDepth = 64;
        options.NumberHandling = JsonNumberHandling.AllowReadingFromString;
    };
} );

Case Strategies

Two naming conventions are supported:

public enum CaseStrategy {
    CamelCase,  // myAwesomeProperty
    SnakeCase   // my_awesome_property
}

Dynamic Object Support

Deserialize to dynamic objects:

var json = "{\"name\":\"John\",\"age\":30}";
dynamic obj = json.FromJson<object>();
Console.WriteLine( obj.name ); // John

Exception Handling

All JSON operations throw JsonParsingException on failure:

try {
    var obj = invalidJson.FromJson<User>();
} catch ( JsonParsingException ex ) {
    Console.WriteLine( $"JSON parsing failed: {ex.Message}" );
    Console.WriteLine( $"Inner exception: {ex.InnerException?.Message}" );
}

String Extensions

Rich set of utilities for string manipulation and analysis.

using Myth.Extensions;

// Remove text
var result = "Hello World".Remove( "World" ); // "Hello "

// Minify (remove all whitespace)
var minified = "Hello   World\n\t".Minify(); // "HelloWorld"

// Change case of first letter
var lower = "Hello".ToFirstLower(); // "hello"
var upper = "hello".ToFirstUpper(); // "Hello"

// Extract text between characters
var text = "The 'quick' brown fox";
var extracted = text.GetStringBetween( '\'' ); // "quick"

// Find words
var sentence = "The quick brown fox";
var word = sentence.GetWordThatContains( "qui" ); // "quick"
var before = sentence.GetWordBefore( "brown" ); // "quick"
var after = sentence.GetWordAfter( "quick" ); // "brown"

// Search operations
var hasAny = "Hello World".ContainsAnyOf( "Hi", "Hello", "Hey" ); // true
var startsWithAny = "Hello World".StartsWithAnyOf( "Hi", "Hello" ); // true

URL Extensions

Encode objects for URL usage:

using Myth.Extensions;

var text = "Hello World";
var encoded = text.EncodeAsUrl(); // "Hello+World"

var flag = true;
var encodedFlag = flag.EncodeAsUrl(); // true (as boolean)

Value Objects

Base class for implementing Domain-Driven Design value objects with structural equality.

using Myth.ValueObjects;

public class Address : ValueObject {
    public string Street { get; }
    public string City { get; }
    public string ZipCode { get; }

    public Address( string street, string city, string zipCode ) {
        Street = street;
        City = city;
        ZipCode = zipCode;
    }

    protected override IEnumerable<object> GetAtomicValues() {
        yield return Street;
        yield return City;
        yield return ZipCode;
    }
}

// Value objects are compared by their values, not reference
var address1 = new Address( "123 Main St", "Springfield", "12345" );
var address2 = new Address( "123 Main St", "Springfield", "12345" );
var address3 = new Address( "456 Oak Ave", "Springfield", "12345" );

Console.WriteLine( address1 == address2 ); // true (same values)
Console.WriteLine( address1 == address3 ); // false (different values)

// Clone value objects
var clone = address1.Clone();

Value Object Benefits

  • Immutability: Encourages immutable design patterns
  • Equality by Value: Automatically handles equality comparison based on properties
  • DDD Alignment: Perfect for domain modeling and tactical DDD patterns
  • Type Safety: Prevents primitive obsession

Constants

Type-safe constants using Ardalis.SmartEnum pattern.

using Myth.ValueObjects;

public class OrderStatus : Constant<OrderStatus, string> {
    public static readonly OrderStatus Pending = new( nameof( Pending ), "PENDING" );
    public static readonly OrderStatus Processing = new( nameof( Processing ), "PROCESSING" );
    public static readonly OrderStatus Completed = new( nameof( Completed ), "COMPLETED" );
    public static readonly OrderStatus Cancelled = new( nameof( Cancelled ), "CANCELLED" );

    private OrderStatus( string name, string value ) : base( name, value ) { }
}

// Usage
var status = OrderStatus.Pending;
string statusValue = status; // Implicit conversion to "PENDING"

// Get from value
var status2 = OrderStatus.FromValue( "PROCESSING" ); // OrderStatus.Processing

// Get from name
var status3 = OrderStatus.FromName( "Completed" ); // OrderStatus.Completed

// List all options
var options = OrderStatus.GetOptions();
// "(Pending): PENDING | (Processing): PROCESSING | (Completed): COMPLETED | (Cancelled): CANCELLED"

// Switch on constants (exhaustive)
var message = status switch {
    var s when s == OrderStatus.Pending => "Order is pending",
    var s when s == OrderStatus.Processing => "Order is being processed",
    var s when s == OrderStatus.Completed => "Order is completed",
    var s when s == OrderStatus.Cancelled => "Order was cancelled",
    _ => throw new InvalidOperationException()
};

Constant Benefits

  • Type Safety: Compile-time safety instead of magic strings/numbers
  • IntelliSense Support: IDE autocomplete for all values
  • Pattern Matching: Works beautifully with C# switch expressions
  • Listing: Easy enumeration of all values
  • Extensibility: Add methods and properties to constants

Service Provider Management

Global service provider management enables cross-library dependency resolution without coupling libraries together.

ASP.NET Core Applications

Use BuildApp() instead of Build() to automatically initialize the global service provider:

var builder = WebApplication.CreateBuilder( args );

builder.Services.AddFlow();
builder.Services.AddGuard();
builder.Services.AddFlowActions( config => { ... } );

var app = builder.BuildApp(); // Instead of builder.Build()

app.UseGuard();
app.Run();

Console Applications / Background Services

Use the global service provider for non-web applications:

var services = new ServiceCollection();
services.AddFlow();
services.AddGuard();
services.AddMyServices();

var serviceProvider = services.BuildServiceProvider();
MythServiceProvider.Initialize( serviceProvider );

// Now all libraries can resolve dependencies
var pipeline = Pipeline.Start( context );

Accessing Global Service Provider

using Myth.ServiceProvider;

// Check if initialized
if ( MythServiceProvider.IsInitialized ) {
    var provider = MythServiceProvider.Current;
}

// Get or throw if not initialized
var requiredProvider = MythServiceProvider.GetRequired();

// Get with fallback
var provider = MythServiceProvider.GetOrFallback( localServiceProvider );

// Try initialize (first-wins pattern)
var initialized = MythServiceProvider.TryInitialize( serviceProvider );

// Force initialize (overwrites existing)
MythServiceProvider.Initialize( serviceProvider );

External Library Integration

External libraries can access registered services:

public class ThirdPartyLibrary {
    public void DoSomething() {
        var provider = ServiceCollectionExtensions.GetGlobalProvider();
        var validator = provider?.GetService<IValidator>();
        if ( validator != null ) {
            // Use Myth libraries without direct coupling
        }
    }
}

Testing Support

Reset the global provider for isolated unit tests:

[Fact]
public void TestWithCleanProvider() {
    MythServiceProvider.Reset();

    var services = new ServiceCollection();
    // ... configure test services
    var provider = services.BuildServiceProvider();
    MythServiceProvider.Initialize( provider );

    // Run test
}

Scoped Services

Pattern for executing operations within automatic service scopes, perfect for transient handlers accessing scoped dependencies like repositories with DbContext.

Setup

Register the scoped service provider pattern once in your application:

var builder = WebApplication.CreateBuilder( args );

builder.Services.AddScopedServiceProvider();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddDbContext<AppDbContext>();

var app = builder.BuildApp();

Usage in Handlers

public class CreateOrderHandler : ICommandHandler<CreateOrderCommand> {
    private readonly IScopedService<IOrderRepository> _repository;
    private readonly IScopedService<IEmailService> _emailService;

    public CreateOrderHandler(
        IScopedService<IOrderRepository> repository,
        IScopedService<IEmailService> emailService ) {
        _repository = repository;
        _emailService = emailService;
    }

    public async Task<CommandResult> HandleAsync(
        CreateOrderCommand command,
        CancellationToken ct ) {

        // Execute with automatic scope management
        var order = await _repository.ExecuteAsync( repo =>
            repo.CreateAsync( command.OrderData, ct )
        );

        // Execute void operations
        await _emailService.ExecuteAsync( email =>
            email.SendOrderConfirmationAsync( order.Id, ct )
        );

        return CommandResult.Success();
    }
}

Synchronous Operations

// With return value
var result = _scopedService.Execute( service =>
    service.GetData()
);

// Void operation
_scopedService.Execute( service =>
    service.ProcessData()
);

Asynchronous Operations

// With return value
var result = await _scopedService.ExecuteAsync( service =>
    service.GetDataAsync()
);

// Void operation
await _scopedService.ExecuteAsync( service =>
    service.ProcessDataAsync()
);

Benefits

  • Automatic Scope Management: No manual scope creation or disposal
  • Lifetime Safety: Access scoped services from transient contexts safely
  • Clean API: Strongly-typed, fluent interface
  • Proper Disposal: Handles both sync and async disposal correctly
  • DDD Alignment: Perfect for CQRS handlers accessing repositories

Pagination

Value objects and interfaces for implementing paginated results.

Pagination Value Object

using Myth.ValueObjects;

// Default pagination (page 1, size 10)
var pagination = Pagination.Default;

// Custom pagination
var customPagination = new Pagination( pageNumber: 2, pageSize: 20 );

// Get all items (single page)
var allItems = Pagination.All;

// ASP.NET Core automatic binding
[HttpGet]
public IActionResult GetOrders( [FromQuery] Pagination pagination ) {
    // Automatically binds from query string: ?$pagenumber=2&$pagesize=20
}

Paginated Results

using Myth.Interfaces.Results;
using Myth.Models.Results;

// Create paginated result
var items = await repository.GetOrdersAsync( pagination );
var total = await repository.GetTotalCountAsync();
var totalPages = ( int )Math.Ceiling( ( double )total / pagination.PageSize );

var result = new Paginated<Order>(
    pageNumber: pagination.PageNumber,
    pageSize: pagination.PageSize,
    totalItems: total,
    totalPages: totalPages,
    items: items
);

// Access properties
Console.WriteLine( $"Page {result.PageNumber} of {result.TotalPages}" );
Console.WriteLine( $"Showing {result.Items.Count()} of {result.TotalItems} items" );

// Return in API
return Ok( result );

IPaginated Interface

Implement custom paginated types:

public class CustomPaginatedResult<T> : IPaginated<T> {
    public int PageNumber { get; set; }
    public int PageSize { get; set; }
    public int TotalPages { get; set; }
    public int TotalItems { get; set; }
    public IEnumerable<T> Items { get; set; }

    // Add custom properties
    public bool HasNextPage => PageNumber < TotalPages;
    public bool HasPreviousPage => PageNumber > 1;
}

Collection Extensions

Helper methods for working with collections.

using Myth.Extensions;

var items = new[] { "apple", "banana", "cherry" };

// Join with separator
var result = items.ToStringWithSeparator( ", " );
// "apple, banana, cherry"

// Custom separator
var result2 = items.ToStringWithSeparator( " | " );
// "apple | banana | cherry"

// Default separator (", ")
var result3 = items.ToStringWithSeparator();
// "apple, banana, cherry"

Architecture Patterns

Domain-Driven Design

Myth.Commons provides essential DDD building blocks:

  • Value Objects: Implement domain value objects with structural equality
  • Constants: Type-safe domain constants and enumerations
  • Pagination: Domain model for paginated queries
// Value Object for Money
public class Money : ValueObject {
    public decimal Amount { get; }
    public string Currency { get; }

    public Money( decimal amount, string currency ) {
        Amount = amount;
        Currency = currency;
    }

    protected override IEnumerable<object> GetAtomicValues() {
        yield return Amount;
        yield return Currency;
    }

    public Money Add( Money other ) {
        if ( Currency != other.Currency )
            throw new InvalidOperationException( "Cannot add money with different currencies" );

        return new Money( Amount + other.Amount, Currency );
    }
}

// Type-safe constants
public class Currency : Constant<Currency, string> {
    public static readonly Currency USD = new( nameof( USD ), "USD" );
    public static readonly Currency EUR = new( nameof( EUR ), "EUR" );
    public static readonly Currency BRL = new( nameof( BRL ), "BRL" );

    private Currency( string name, string value ) : base( name, value ) { }
}

CQRS Integration

Perfect for CQRS patterns with scoped service management:

public class GetOrdersQueryHandler : IQueryHandler<GetOrdersQuery, IPaginated<Order>> {
    private readonly IScopedService<IOrderRepository> _repository;

    public GetOrdersQueryHandler( IScopedService<IOrderRepository> repository ) {
        _repository = repository;
    }

    public async Task<QueryResult<IPaginated<Order>>> HandleAsync(
        GetOrdersQuery query,
        CancellationToken ct ) {

        var result = await _repository.ExecuteAsync( async repo => {
            var items = await repo.GetOrdersAsync( query.Pagination, ct );
            var total = await repo.GetTotalCountAsync( ct );

            return new Paginated<Order>(
                query.Pagination.PageNumber,
                query.Pagination.PageSize,
                total,
                ( int )Math.Ceiling( ( double )total / query.Pagination.PageSize ),
                items
            );
        } );

        return QueryResult<IPaginated<Order>>.Success( result );
    }
}

Clean Architecture

Supports Clean Architecture principles:

  • Infrastructure Independence: JSON serialization without external dependencies
  • Framework Independence: Works with any .NET application type
  • Testability: Easy to mock and test with clear interfaces
  • Separation of Concerns: Each utility focused on single responsibility

Best Practices

JSON Serialization

  1. Configure global JSON settings once at application startup
  2. Use per-operation settings only when needed
  3. Handle JsonParsingException for robust error handling
  4. Use interface converters for polymorphic types

Value Objects

  1. Make value objects immutable
  2. Override GetAtomicValues() to include all properties that define equality
  3. Consider validation in constructor
  4. Use for domain concepts, not just data transfer

Constants

  1. Use for domain-specific enumerations
  2. Prefer constants over magic strings/numbers
  3. Add domain methods to constant classes
  4. Use with pattern matching for exhaustive checks

Service Provider

  1. Initialize global provider once at application startup
  2. Use BuildApp() for ASP.NET Core applications
  3. Use TryInitialize() for library code (first-wins pattern)
  4. Reset provider in tests for isolation

Scoped Services

  1. Register AddScopedServiceProvider() once per application
  2. Use for accessing scoped dependencies from transient contexts
  3. Perfect for CQRS handlers and background services
  4. Automatic disposal - no manual scope management needed

Dependencies

  • Ardalis.SmartEnum 8.0.0 - For type-safe constants
  • Microsoft.Extensions.DependencyInjection 8.0.0 - For DI support
  • System.Text.Json 8.0.5 - For JSON operations

Requirements

  • .NET 8.0 or later

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the Apache License 2.0 - see the LICENSE for details.

Support

For issues, questions, or contributions, please visit the GitLab repository.

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.
  • net10.0

    • No dependencies.

NuGet packages (13)

Showing the top 5 NuGet packages that depend on Myth.Commons:

Package Downloads
Harpy.Domain

Basis for the domain layer of the Harpy Framework

Myth.Specification

Query specification pattern for encapsulating business rules in readable, composable, and testable specifications. Supports filtering, sorting, pagination, and conditional logic for clean query construction.

Myth.Repository

Generic repository pattern interfaces with async support, specification integration, and pagination. Provides read/write separation, CRUD operations, and extensible repository contracts for clean data access architecture.

Myth.Rest

Fluent REST client with circuit breaker, retry policies, certificate support, and object pooling. Provides resilient HTTP communication with configurable error handling and response mapping.

Harpy.Presentation

Basis for the presentation layer of the Harpy Framework

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.1-preview.8 0 11/22/2025
4.0.1-preview.7 0 11/22/2025
4.0.1-preview.6 0 11/22/2025
4.0.1-preview.5 0 11/21/2025
4.0.1-preview.4 0 11/21/2025
4.0.1-preview.3 16 11/21/2025
4.0.1-preview.2 46 11/21/2025
4.0.1-preview.1 57 11/21/2025
4.0.0 206 11/20/2025
4.0.0-preview.3 173 11/19/2025
4.0.0-preview.2 83 11/15/2025
4.0.0-preview.1 105 11/15/2025
3.10.0 307 11/15/2025
3.0.5-preview.15 145 11/14/2025
3.0.5-preview.14 217 11/12/2025
3.0.5-preview.13 221 11/12/2025
3.0.5-preview.12 229 11/11/2025
3.0.5-preview.11 226 11/11/2025
3.0.5-preview.10 221 11/11/2025
3.0.5-preview.9 220 11/10/2025
3.0.5-preview.8 96 11/8/2025
3.0.5-preview.7 89 11/8/2025
3.0.5-preview.6 86 11/8/2025
3.0.5-preview.5 87 11/8/2025
3.0.5-preview.4 65 11/7/2025
3.0.5-preview.3 135 11/4/2025
3.0.5-preview.2 140 11/4/2025
3.0.5-preview.1 139 11/4/2025
3.0.4 366 11/3/2025
3.0.4-preview.19 73 11/2/2025
3.0.4-preview.17 69 11/1/2025
3.0.4-preview.16 71 11/1/2025
3.0.4-preview.15 65 10/31/2025
3.0.4-preview.14 134 10/31/2025
3.0.4-preview.13 130 10/30/2025
3.0.4-preview.12 124 10/23/2025
3.0.4-preview.11 123 10/23/2025
3.0.4-preview.10 120 10/23/2025
3.0.4-preview.9 117 10/23/2025
3.0.4-preview.8 116 10/22/2025
3.0.4-preview.6 116 10/21/2025
3.0.4-preview.5 117 10/21/2025
3.0.4-preview.4 121 10/20/2025
3.0.4-preview.3 119 10/20/2025
3.0.4-preview.2 42 10/18/2025
3.0.4-preview.1 122 10/7/2025
3.0.3 285 8/30/2025
3.0.2 179 8/23/2025
3.0.2-preview.4 128 8/21/2025
3.0.2-preview.3 100 8/16/2025
3.0.2-preview.1 77 5/23/2025
3.0.1 4,133 3/12/2025
3.0.1-preview.2 145 3/11/2025
3.0.1-preview.1 92 2/5/2025
3.0.0.2-preview 202 12/10/2024
3.0.0.1-preview 209 6/29/2024
3.0.0 4,646 12/10/2024
3.0.0-preview 215 6/28/2024
2.0.0.16 16,202 12/15/2023
2.0.0.15 367 12/15/2023
2.0.0.11 5,585 8/11/2022
2.0.0.10 3,033 7/20/2022
2.0.0.9 3,126 7/15/2022
2.0.0.8 3,146 7/12/2022
2.0.0.7 3,064 6/20/2022
2.0.0.6 3,176 5/23/2022
2.0.0.5 3,162 5/18/2022
2.0.0 3,274 2/17/2022
1.0.0.8 2,605 12/8/2020
1.0.0.8-stage-02 1,186 12/7/2020
1.0.0.8-stage-01 1,446 9/25/2020
1.0.0.7 3,863 7/28/2020
1.0.0.7-stage-03 1,229 7/28/2020
1.0.0.7-stage-02 1,113 7/23/2020
1.0.0.7-stage-01 1,066 7/21/2020
1.0.0.6 2,393 6/28/2020
1.0.0.4 2,235 6/27/2020
1.0.0.3 587 6/27/2020
1.0.0.2 5,284 6/25/2020
1.0.0.1 1,016 6/25/2020
1.0.0 590 6/25/2020