Siemens.AspNet.MinimalApi.Sdk.Contracts 0.1.0-alpha.353

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

Siemens.AspNet.MinimalApi.Sdk.Contracts

The Siemens.AspNet.MinimalApi.Sdk.Contracts NuGet package defines essential interfaces, attributes, and base classes designed to standardize common functionalities required for building Minimal APIs. These abstractions support dependency injection, JSON serialization, difference tracking, and advanced validation scenarios.


📖 Overview

This package provides foundational contracts that facilitate a structured, extensible, and maintainable Minimal API architecture.

✅ Key Abstractions

  • 📐 Activators

    • IActivator
    • IAsyncActivator<T>
    • ICustomActivator<T>
  • 📦 JSON Serialization & Differencing

    • IJsonSerializer for robust serialization and deserialization.
    • IJsonDiffer for detailed JSON comparison and difference detection.
  • ✔️ Advanced Validation

    • IAttributeValidator for recursive and attribute-driven validation.

    • CustomValidatorBase<T, TAttribute> for custom synchronous validation logic.

    • AsyncCustomValidatorBase<T, TAttribute> for asynchronous custom validation.

    • Request validation abstractions:

      • RequestValidator<TRequest> and AsyncRequestValidator<TRequest> for complete object validation.
      • PatchRequestValidator<TRequest> and AsyncPatchRequestValidator<TRequest> specifically tailored for PATCH scenarios.

📦 Installation

Using the .NET CLI

dotnet add package Siemens.AspNet.MinimalApi.Sdk.Contracts

⚡ Examples

Activator Usage

public class ExampleService
{
    private readonly IActivator _activator;

    public ExampleService(IActivator activator)
    {
        _activator = activator;
    }

    public async Task<MyClass> CreateMyClassAsync()
    {
        return await _activator.CreateInstanceAsync<MyClass>();
    }
}

JSON Differ Example

public void CheckJsonDifferences(IJsonDiffer jsonDiffer)
{
    var differences = jsonDiffer.FindDifferences("{\"name\":\"John\"}", "{\"name\":\"Jane\"}");
    foreach (var diff in differences)
    {
        Console.WriteLine($"Property: {diff.MemberPath}, Difference: {diff.MismatchType}");
    }
}

Custom Validation with Attributes

internal static class AddAlphaNumericValidatorExtension
{
    internal static void AddAlphaNumericValidator(this IServiceCollection services)
    {
        services.AddSingletonIfNotExists<ICustomValidator, AlphaNumericValidator>();
    }
}

public class AlphaNumericAttribute(params string[] sampleValues) : CustomValidationAttribute<AlphaNumericValidator>(string.Empty, sampleValues);

public sealed class AlphaNumericValidator : CustomValidatorBase<string, AlphaNumericAttribute>
{
    protected override IEnumerable<ValidationErrorDetailsBase> Validate(PropertyInfo propertyInfo,
                                                                        AlphaNumericAttribute attribute,
                                                                        string? source)
    {
        if (source == null)
        {
            yield break;
        }

        if (source.Any(ch => !char.IsLetterOrDigit(ch)))
        {
            var sampleValues = attribute.SampleValues.Any() ? attribute.SampleValues : ["sample123"];

            yield return new ValidationErrorDetailsBase
            {
                Errors = [$"{propertyInfo.Name} must be alphanumeric."],
                Samples = sampleValues
            };
        }
    }
}

Complex-Custom Validation with Attributes

We provide another level of validation if you have an data historie to compare current value against your privious value. (Historical Validation)

internal static class AddIsNotChangeableAfterDeploymentValidatorExtension
    {
        internal static void AddIsNotChangeableAfterDeploymentValidator(this IServiceCollection services)
        {
            services.AddSingletonIfNotExists<IComplexCustomValidator, IsNotChangableAfterDeploymentValidator>();
        }
    }

    internal sealed class IsNotChangeableAfterDeploymentAttribute() : ComplexCustomValidationAttribute<IsNotChangableAfterDeploymentValidator>(string.Empty, [])
    {
    }

    internal sealed class IsNotChangableAfterDeploymentValidator() : ComplexCustomValidator<AwsS3Bucket, string?, IsNotChangeableAfterDeploymentAttribute>
    {
        protected override IEnumerable<ValidationErrorDetailsBase> Validate(PropertyInfo propertyInfo,
                                                                            IsNotChangeableAfterDeploymentAttribute attribute,
                                                                            AwsS3Bucket capability,
                                                                            string? currentValue,
                                                                            string? lastValue)
        {
            if (capability.IsDeployed.IsFalse())
            {
                yield break;
            }

            if (currentValue != lastValue)
            {
                yield return new ValidationErrorDetailsBase
                {
                    Errors = [$"You are not allowed to change the: {propertyInfo.Name} after the capability was deployed ! This would cause in critical male function of your project"],
                    Samples = [currentValue]
                };
            }
        }
    }

Request Validator (sync)

This request validator shows a sample without attribute validation.

public static class AddCreateFormsConfigurationRequestValidatorExtension
{
    internal static void AddCreateFormsConfigurationRequestValidator(this IServiceCollection services,
                                                                        IConfiguration configuration)
    {       
        services.AddFormsConfigurationSettings(configuration);

        services.AddSingletonIfNotExists<CreateFormsConfigurationRequestValidator>();
    }
}

internal sealed class CreateFormsConfigurationRequestValidator : RequestValidator<CreateFormsConfigurationRequest>
{
    protected override IEnumerable<PropertyValidationResult> GetValidationErrors(CreateFormsConfigurationRequest request)
    {
        // Your validation code here
        if (request.FormsId.IsNull())
        {
            var errorDetails = new ValidationErrorDetails
                                {
                                    CurrentValue = request.FormsId,
                                    Errors = [$"{nameof(request.FormsId)} must not be null. Only GUID or a long is valid for the {nameof(request.FormsId)}"],
                                    Samples = ["a1b2c3d4-e5f6-7890-1234-567890abcdef", "1"]
                                };

            yield return new PropertyValidationResult(nameof(request.FormsId), errorDetails);
        }
    }
}

Async Request Validator

In this sample the async validator is used to validate a CreateDeploymentRequest object. The validator checks for the presence of required properties and validates them using the IAttributeValidator interface.

public sealed record CreateDeploymentRequest
{
    [ValidEnum]
    public required Stage Stage { get; init; }
};
internal static class AddCreateDeploymentRequestValidatorExtension
{
    internal static void AddCreateDeploymentRequestValidator(this IServiceCollection services)
    {
        services.AddSingletonIfNotExists<CreateDeploymentRequestValidator>();
    }
}

internal sealed class CreateDeploymentRequestValidator(IAttributeValidator attributeValidator) : AsyncRequestValidator<CreateDeploymentRequest>
{
    protected override async IAsyncEnumerable<PropertyValidationResult> GetValidationErrorsAsync(CreateDeploymentRequest request,
                                                                                                    [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        var errors = await attributeValidator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
        foreach (var propertyValidationResult in errors)
        {
            yield return propertyValidationResult;
        }
    }
}

Injectable Endpoint

The Injectable Endpoint feature introduces a clean, modular way to register Minimal API endpoints directly through dependency injection. Instead of mapping endpoints manually inside Program.cs, you can now inject them into your application as services.

This makes your API endpoints:

  • Discoverable – any registered IEndpoint implementation will be automatically picked up.
  • Modular – endpoints are defined in self-contained classes.
  • No application.Map calls necessary in Program.cs or somewhere else → resolved automatically
  • Provides the option in future to configure which endpoints are available and which not.

Interface:

public interface IEndpoint
{
    void Map(IEndpointRouteBuilder versionBasePath);
}

Sample:

internal static class AddCreateCapabilityEndpointExtension
{
    internal static void AddCreateCapabilityEndpoint(this IServiceCollection services)
    {
        services.AddSingletonIfNotExists<IEndpoint, CreateCapabilityEndpoint>();
    }
}
    
internal class CreateCapabilityEndpoint(CapabilityProvider capabilityProvider) : IEndpoint
{
    public void Map(IEndpointRouteBuilder endpoints)
    {
        // Your endpoint registration logic here :)
        endpoints.MapPost("/capabilities", async (CreateCapabilityRequest request, CancellationToken cancellationToken) =>
        {
            var capability = await capabilityProvider.CreateAsync(request, cancellationToken);
            return Results.Created($"/capabilities/{capability.Id}", capability);
        })
    }
}

Registration:

As you can see no Map necessary. We just register the endpoint

namespace Sdc.Console.Api.Capabilities.V1
{
    internal static class CreateStartup
    {
        internal static void AddCreate(this IServiceCollection services,
                                       IConfiguration configuration)
        {
            services.AddCreateCapabilityCommand(configuration);
            services.AddCreateCapabilityEndpoint();
        }
    }
}

AWS DynamoDB Custom Converters

The Siemens.AspNet.MinimalApi.Sdk already provides out of the box some helpers for the most common types. You can use them directly or implement your own converters.

Converter Name Description
DateTimeOffsetConverter Converts DateTimeOffset values to and from string format (typically ISO 8601) for DynamoDB.
DictionaryStringObjectConverter Handles conversion of Dictionary<string, object> to a DynamoDB-compatible format.
DictionaryStringObjectNullableConverter Similar to DictionaryStringObjectConverter but allows nullable dictionary handling.
ImmutableDictionaryStringObjectNullableConverter Converts ImmutableDictionary<string, object?> to a format compatible with DynamoDB, supporting null values.
TimeSpanConverter Serializes TimeSpan values as string and deserializes them back. Useful for time duration storage in DynamoDB.

Sample:

[DynamoDBTable("Capability")]
public record CapabilityEntity
{
    [DynamoDBHashKey]
    public required Guid Id { get; init; }

    [DynamoDBRangeKey]
    public required string DeploymentId { get; init; } = CapabilityConstants.DefaultDeploymentId;

    [DynamoDBProperty(typeof(DateTimeOffsetConverter))]
    public required DateTimeOffset LastUpdatedDate { get; init; } = DateTimeOffset.UtcNow;
}

AWS Dynamo Entity Mapper (POC)

The IDynamoEntityMapper brings already most common converter with it (Siemens.AspNet.MinimalApi.Sdk). You can just use it. In exception cases, you can implement your own converter by implementing IDynamoTypeConverter or IAsyncDynamoTypeConverter.

Converter Name Description
EnumToStringConverter Converts enum values to their string representation and vice versa. Useful for storing enums as strings in DynamoDB.
EnumerableToImmutableConverter Converts IEnumerable<T> to ImmutableList<T> for safe, immutable handling of collections during mapping.
ImmutableToListConverter Converts ImmutableList<T> to List<T> to support serialization or mutable collection use cases.
TimeSpanToStringConverter Serializes TimeSpan values as ISO 8601-like strings and parses them back. Enables human-readable time span storage.
public sealed class MyHandler(IDynamoEntityMapper mapper)
{
    public async Task<MyDto> HandleAsync(object rawData, CancellationToken cancellationToken)
    {
        return await mapper.ConvertAsync<MyDto>(rawData, cancellationToken);
    }
}

Custom property converter example:

internal static class AddTimeSpanToStringConverterExtension
{
    internal static void AddTimeSpanToStringConverter(this IServiceCollection services)
    {
        services.AddSingletonIfNotExists<IDynamoTypeConverter, TimeSpanToStringConverter>();
    }
}

internal sealed class TimeSpanToStringConverter : IDynamoTypeConverter
{
    public bool CanConvert(Type source,
                            Type target)
    {
        var canConvert = source == typeof(TimeSpan) &&
                            target == typeof(string);

        return canConvert;
    }

    public object? ConvertObject(object? source,
                                    Type targetType)
    {
        return source?.ToString();
    }
}

📌 Usage & Best Practices

  • Leverage IActivator for instance creation with dependency injection.
  • Leverage IAsyncActivator for instance creation with dependency injection in asynchronous contexts.
  • Utilize IJsonDiffer for tracking JSON changes in PATCH requests.
  • Implement custom validation logic by extending provided base validator classes.
  • Implement IAttributeValidator custom for attribute-driven validation scenarios.
  • Simplified attribut-based validation by the IAttributeValidator interface.
  • Use IDynamoTypeConverter or IAsyncDynamoTypeConverter to encapsulate DynamoDB-safe conversions.
  • Register custom converters and provide an implementation of IDynamoEntityMapper to centralize conversion logic.

📚 Documentation

Additional details and examples are available in the repository documentation and upcoming online resources.


📢 Contributing

Your contributions and feedback are welcomed! Please create issues or pull requests to enhance this package.

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

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Siemens.AspNet.MinimalApi.Sdk.Contracts:

Package Downloads
Siemens.AspNet.MinimalApi.Sdk

A library which contains following functions: - Siemens.AspNet.MinimalApi.Sdk

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-alpha.355 0 12/10/2025
0.1.0-alpha.354 255 12/8/2025
0.1.0-alpha.353 621 12/2/2025
0.1.0-alpha.349 597 11/28/2025
0.1.0-alpha.337 125 11/27/2025
0.1.0-alpha.336 760 11/24/2025
0.1.0-alpha.335 109 11/23/2025
0.1.0-alpha.334 307 11/23/2025
0.1.0-alpha.333 106 11/23/2025
0.1.0-alpha.332 1,771 11/11/2025
0.1.0-alpha.331 201 11/10/2025
0.1.0-alpha.330 346 11/7/2025
0.1.0-alpha.329 124 11/7/2025
0.1.0-alpha.328 108 11/7/2025
0.1.0-alpha.327 152 11/6/2025
0.1.0-alpha.326 148 11/6/2025
0.1.0-alpha.314 144 11/4/2025
0.1.0-alpha.313 139 11/4/2025
0.1.0-alpha.311 145 11/3/2025
0.1.0-alpha.309 789 10/31/2025
0.1.0-alpha.307 169 10/31/2025
0.1.0-alpha.306 124 10/31/2025
0.1.0-alpha.305 324 10/27/2025
0.1.0-alpha.303 570 10/22/2025
0.1.0-alpha.302 128 10/21/2025
0.1.0-alpha.301 125 10/21/2025
0.1.0-alpha.300 199 10/20/2025
0.1.0-alpha.299 96 10/18/2025
0.1.0-alpha.298 73 10/18/2025
0.1.0-alpha.297 70 10/18/2025
0.1.0-alpha.296 69 10/18/2025
0.1.0-alpha.294 96 10/17/2025
0.1.0-alpha.293 234 10/8/2025
0.1.0-alpha.292 125 10/8/2025
0.1.0-alpha.290 139 10/8/2025
0.1.0-alpha.289 126 10/7/2025
0.1.0-alpha.284 149 10/7/2025
0.1.0-alpha.283 274 9/19/2025
0.1.0-alpha.282 684 9/19/2025
0.1.0-alpha.281 322 9/16/2025
0.1.0-alpha.280 269 9/16/2025
0.1.0-alpha.279 268 9/16/2025
0.1.0-alpha.278 265 9/16/2025
0.1.0-alpha.275 55,533 9/3/2025
0.1.0-alpha.274 366 9/2/2025
0.1.0-alpha.273 1,286 9/1/2025
0.1.0-alpha.272 129 9/1/2025
0.1.0-alpha.271 181 8/29/2025
0.1.0-alpha.270 164 8/29/2025
0.1.0-alpha.269 164 8/29/2025
0.1.0-alpha.268 168 8/29/2025
0.1.0-alpha.267 176 8/27/2025
0.1.0-alpha.266 204 8/27/2025
0.1.0-alpha.264 269 8/22/2025
0.1.0-alpha.263 86 8/22/2025
0.1.0-alpha.262 82 8/22/2025
0.1.0-alpha.261 96 8/22/2025
0.1.0-alpha.260 104 8/22/2025
0.1.0-alpha.259 108 8/22/2025
0.1.0-alpha.258 206 8/19/2025
0.1.0-alpha.257 204 8/18/2025
0.1.0-alpha.246 166 8/14/2025
0.1.0-alpha.245 129 8/14/2025
0.1.0-alpha.244 176 8/14/2025
0.1.0-alpha.243 129 8/14/2025
0.1.0-alpha.238 131 8/12/2025
0.1.0-alpha.237 496 8/6/2025
0.1.0-alpha.236 244 8/5/2025
0.1.0-alpha.235 210 8/5/2025
0.1.0-alpha.234 214 8/5/2025
0.1.0-alpha.233 184 8/4/2025
0.1.0-alpha.232 195 8/4/2025
0.1.0-alpha.231 89 8/1/2025
0.1.0-alpha.230 82 8/1/2025
0.1.0-alpha.229 104 7/31/2025
0.1.0-alpha.228 104 7/31/2025
0.1.0-alpha.227 106 7/31/2025
0.1.0-alpha.225 104 7/31/2025
0.1.0-alpha.224 108 7/30/2025
0.1.0-alpha.222 332 7/16/2025
0.1.0-alpha.219 174 7/14/2025
0.1.0-alpha.217 102 7/11/2025
0.1.0-alpha.212 175 7/8/2025
0.1.0-alpha.211 176 7/3/2025
0.1.0-alpha.207 128 7/3/2025
0.1.0-alpha.206 312 6/30/2025
0.1.0-alpha.205 110 6/27/2025
0.1.0-alpha.202 126 6/27/2025
0.1.0-alpha.200 107 6/27/2025
0.1.0-alpha.198 110 6/27/2025
0.1.0-alpha.196 111 6/27/2025
0.1.0-alpha.195 112 6/27/2025
0.1.0-alpha.194 111 6/27/2025
0.1.0-alpha.193 110 6/27/2025
0.1.0-alpha.192 115 6/27/2025
0.1.0-alpha.191 111 6/27/2025
0.1.0-alpha.189 131 6/26/2025
0.1.0-alpha.188 185 6/26/2025
0.1.0-alpha.187 125 6/26/2025
0.1.0-alpha.186 150 6/26/2025
0.1.0-alpha.185 134 6/26/2025
0.1.0-alpha.184 136 6/26/2025
0.1.0-alpha.183 134 6/26/2025
0.1.0-alpha.182 128 6/26/2025
0.1.0-alpha.181 143 6/25/2025
0.1.0-alpha.180 151 6/24/2025
0.1.0-alpha.179 130 6/23/2025
0.1.0-alpha.178 215 6/23/2025
0.1.0-alpha.176 136 6/23/2025
0.1.0-alpha.174 147 6/19/2025
0.1.0-alpha.173 172 6/19/2025
0.1.0-alpha.172 135 6/17/2025
0.1.0-alpha.171 231 6/16/2025
0.1.0-alpha.169 134 6/16/2025
0.1.0-alpha.165 432 6/13/2025
0.1.0-alpha.164 244 6/13/2025
0.1.0-alpha.163 241 6/13/2025
0.1.0-alpha.160 278 6/12/2025
0.1.0-alpha.159 375 6/11/2025
0.1.0-alpha.158 284 6/11/2025
0.1.0-alpha.143 275 6/11/2025
0.1.0-alpha.142 274 6/11/2025
0.1.0-alpha.140 276 6/11/2025
0.1.0-alpha.139 340 6/10/2025
0.1.0-alpha.138 268 6/9/2025
0.1.0-alpha.137 68 6/7/2025
0.1.0-alpha.136 60 6/7/2025
0.1.0-alpha.135 93 6/6/2025
0.1.0-alpha.134 96 6/6/2025
0.1.0-alpha.130 140 6/5/2025
0.1.0-alpha.129 134 6/4/2025
0.1.0-alpha.128 131 6/4/2025
0.1.0-alpha.122 197 6/3/2025
0.1.0-alpha.121 146 6/1/2025
0.1.0-alpha.120 104 6/1/2025
0.1.0-alpha.118 142 5/28/2025
0.1.0-alpha.117 138 5/28/2025
0.1.0-alpha.116 179 5/28/2025
0.1.0-alpha.115 146 5/26/2025
0.1.0-alpha.114 170 5/22/2025
0.1.0-alpha.112 138 5/21/2025
0.1.0-alpha.111 135 5/20/2025
0.1.0-alpha.108 198 5/19/2025
0.1.0-alpha.104 491 5/18/2025
0.1.0-alpha.102 369 5/14/2025
0.1.0-alpha.101 220 5/14/2025
0.1.0-alpha.100 225 5/12/2025
0.1.0-alpha.99 251 5/12/2025
0.1.0-alpha.98 71 5/10/2025
0.1.0-alpha.97 63 5/10/2025
0.1.0-alpha.86 162 5/8/2025
0.1.0-alpha.85 132 5/8/2025
0.1.0-alpha.84 139 5/8/2025
0.1.0-alpha.82 169 5/7/2025
0.1.0-alpha.81 141 5/6/2025