Xpandables.Net 9.3.2

dotnet add package Xpandables.Net --version 9.3.2
                    
NuGet\Install-Package Xpandables.Net -Version 9.3.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="Xpandables.Net" Version="9.3.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Xpandables.Net" Version="9.3.2" />
                    
Directory.Packages.props
<PackageReference Include="Xpandables.Net" />
                    
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 Xpandables.Net --version 9.3.2
                    
#r "nuget: Xpandables.Net, 9.3.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.
#addin nuget:?package=Xpandables.Net&version=9.3.2
                    
Install Xpandables.Net as a Cake Addin
#tool nuget:?package=Xpandables.Net&version=9.3.2
                    
Install Xpandables.Net as a Cake Tool

Introduction

Provides with useful interfaces contracts in .Net 9.0 and some implementations mostly following the spirit of SOLID principles, Commands... The library is strongly-typed, which means it should be hard to make invalid requests and it also makes it easy to discover available methods and properties though IntelliSense.

Feel free to fork this project, make your own changes and create a pull request.

This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.

Getting Started

Optional

The Optional< T> is a C# implementation of an optional value that may or may not be present. This implementation is part of the Xpandables.Net library and is designed to work with .NET 9. The Optional< T> struct provides a way to represent optional values, similar to the Nullable< T> type but for reference types and value types alike.

Features

  • Is a struct, immutable, a generic type, so it can hold a value of any type.
  • Represents an optional value that may or may not be present.
  • Provides a way to work with optional values in a functional way.
  • Provides a way to create an optional value from a value or from an empty value.
  • Supports JSON serialization through OptionalJsonConverterFactory.
  • Implements IEnumerable< T> to allow iteration over the optional value.

Usage

Creating an Optional Value

You can create an Optional< T> value using helpers or implicit conversion.


using Xpandables.Net.Optionals;

// Creating an optional with a value
Optional<int> optionalWithValue = Optional.Some(42);

// Creating an empty optional
Optional<int> emptyOptional = Optional.Empty<int>();

Checking for Value Presence

You can check if the optional has a value using the IsEmpty or IsNotEmpty properties.


if (emptyOptional.IsEmpty)
{
    Console.WriteLine("Optional is empty.");
}

if (optionalWithValue.IsNotEmpty)
{
    Console.WriteLine("Optional is not empty.");
}

Accessing the Value

You can access the value of the Optional using the Value property. Note that accessing the value when it is not present will throw an InvalidOperationException.


try
{
    int value = optionalWithValue.Value;
    Console.WriteLine($"Value: {value}");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.Message);
}


Iterating over the Value

Since Optional< T> implements IEnumerable< T>, you can use it in a foreach loop.


foreach (var value in optionalWithValue)
{
    Console.WriteLine($"Value: {value}");
}

foreach (var value in emptyOptional)
{
    Console.WriteLine($"Value: {value}"); // This will not execute
}

Using the Map Method

The Map method allows you to act on the value inside the optional if it is present.


Optional<int> optional = 42;
Optional<int> mappedOptional = optional.Map(value => value * 2);

if (mappedOptional.IsNotEmpty)
{
    Console.WriteLine($"Mapped Value: {mappedOptional.Value}"); 
    // Output: Mapped Value: 84
}

Using the Bind Method

The Bind method allows you to transform the value inside the optional to another type and return a new optional.


Optional<string> optional = Optional.Some("Hello Word");
Optional<int> boundOptional = optional.Bind(value => value.Length);

if (boundOptional.HasValue)
{
    Console.WriteLine($"Bound Value: {boundOptional.Value}"); 
    // Output: Bound Value: 10
}

Using the Empty Method

The Empty method allows you to provide a value if the current optional is empty.


public string GetName()
{
    Optional<Name> optional = function call;
    return optional
        .Empty("No Name");

    // If the optional has a value, the function value will be returned.
    // Otherwise, the Empty value will be returned.
}

JSON Serialization

The Optional< T> struct is decorated with OptionalJsonConverterFactory to support JSON serialization.


using System.Text.Json;

var optional = Optional.Some(42);
string json = JsonSerializer.Serialize(optional);
Console.WriteLine(json); // Output: {"Value":42}

var deserializedOptional = JsonSerializer.Deserialize<Optional<int>>(json);
Console.WriteLine(deserializedOptional.IsNotEmpty); // True
Console.WriteLine($"Deserialized Value: {deserializedOptional.Value}"); 
// Output: Deserialized Value: 42

// anonymous type
var anonymous = Optional.Some(new { Name = "Hello World" });
string anonymousJson = JsonSerializer.Serialize(anonymous);
Console.WriteLine(anonymousJson); // Output: {"Name":"Hello World"}

var deserializedAnonymous = DeserializeAnonymousType(anonymousJson, anonymous);
// or you can use an anonymous instance
// var deserializedAnonymous = DeserializeAnonymousType(
//      anonymousJson, 
//      Optional.Some(new { Name = string.Empty }));
Console.WriteLine($"Deserialized Anonymous Value: {deserializedAnonymous.Value.Name}"); 
// Output: Deserialized Anonymous Value: Name: Hello World

static T? DeserializeAnonymousType<T>(
    string json, T _, JsonSerializerOptions? options = default) =>
    JsonSerializer.Deserialize<T>(json, options);


Chaining Methods in a Fluent Manner

You can chain the methods of Optional< T> in a fluent manner to produce the expected result.


Optional<int> optional = Optional.Some(42);

Optional<string> result = optional
    .Map(value => value * 2) // Double the value
    .Bind(value => Optional.Some(value.ToString())) // Convert to string
    .Empty(() => "Default Value"); // Provide a default value if empty

if (result.IsNotEmpty)
{
    Console.WriteLine($"Result: {result.Value}"); // Output: Result: 84
}
else
{
    Console.WriteLine("Result is empty.");
}


ExecutionResult and ExecutionResults

Overview

The ExecutionResult and ExecutionResults classes are part of the Xpandables.Net.Executions namespace. They provide a structured way to handle the results of operations, encapsulating both success and failure scenarios with detailed information.

ExecutionResult

The ExecutionResult interface represents the result of an execution. It includes properties for status code, title, detail, location, result, errors, headers, and extensions. It also provides methods to check if the execution was successful and to retrieve any associated exceptions.

ExecutionResults

The ExecutionResults class provides static methods to create instances of ExecutionResult for both success and failure scenarios. It includes methods to set various HTTP status codes and to include additional details like titles, details, locations, and errors.

Usage

Creating a Success Execution Result

To create a success execution result, you can use the Success method from the ExecutionResults class. You can specify the status code, result, and other details.


using System.Net;
using Xpandables.Net.Executions;

public class SampleUsage
{
    public ExecutionResult CreateSuccessResult()
    {
        return ExecutionResults.Success(HttpStatusCode.OK)
            .WithLocation(new Uri("http://example.com"))
            .Build();
    }

    public ExecutionResult<string> CreateSuccessResultWithData()
    {
        return ExecutionResults.Success("Success Data", HttpStatusCode.OK)
            .WithLocation(new Uri("http://example.com"))
            .Build();
    }
}

Creating a Failure Execution Result

To create a failure execution result, you can use the Failure method from the ExecutionResults class. You can specify the status code, errors, and other details.


using System.Net;
using Xpandables.Net.Executions;

public class SampleUsage
{
    public ExecutionResult CreateFailureResult()
    {
        return ExecutionResults.Failure(HttpStatusCode.BadRequest)
            .WithTitle("Execution Failed")
            .WithDetail("The execution failed due to bad request.")
            .WithError("ErrorKey", "ErrorMessage")
            .Build();
    }

    public ExecutionResult<string> CreateFailureResultWithData()
    {
        return ExecutionResults.Failure<string>(HttpStatusCode.BadRequest)
            .WithTitle("Execution Failed")
            .WithDetail("The execution failed due to bad request with data.")
            .WithError("ErrorKey", "ErrorMessage")
            .Build();
    }
}

Using Predefined Methods

The ExecutionResults class also provides predefined methods for common HTTP status codes like Ok, Created, NoContent, NotFound, BadRequest, Conflict, Unauthorized, InternalServerError, and ServiceUnavailable.


using Xpandables.Net.Executions;

public class SampleUsage
{
    public ExecutionResult CreateOkResult()
    {
        return ExecutionResults.Ok()
            .Build();
    }

    public ExecutionResult<string> CreateNotFoundResult()
    {
        return ExecutionResults.NotFound<string>()
            .WithTitle("Resource Not Found")
            .WithDetail("The requested resource was not found.")
            .Build();
    }
}

The ExecutionResult and ExecutionResults classes provide a flexible and structured way to handle execution results in your application. By using these classes, you can ensure that your operations return consistent and detailed results, making it easier to handle both success and failure scenarios.

Overview

The IRestClient interface and related classes in the Xpandables.Net.Executions.Rests namespace provide a structured way to handle HTTP client requests and responses. These classes and interfaces allow you to configure, send, and process HTTP requests with detailed options and builders.

IRestClient

The IRestClient interface provides methods to handle HTTP client requests using a typed client HTTP client. It supports sending requests that do not return a response, requests that return a response of a specific type, and requests that return a stream that can be async-enumerated.

IRestAttributeBuilder

The IRestAttributeBuilder interface defines a builder for creating RestAttribute. This interface takes priority over the RestAttribute.

RestAttribute

The RestAttribute class is an attribute used to configure options for HTTP client requests. It should decorate implementations of IRestRequest, IRestRequest<TResponse>, or IRestRequestStream<TResponse> to be used with IRestClient.

Usage

Creating and Sending a Simple Request

To create and send a simple request using IRestClient, you can define a request class and decorate it with RestAttribute.


using System.Net; 
using Xpandables.Net.Executions.Rests;

[RestGet("/api/data")] 
public sealed record GetDataRequest : IRestString; // IRestString inherits IRestRequest


public class SampleUsage 
{ 
    private readonly IRestClient _restClient;
    public SampleUsage(IRestClient restClient)
    {
        _restClient = restClient;
    }

    public async Task SendRequestAsync()
    {
        var request = new GetDataRequest();
        RestResponse response = await _restClient.SendAsync(request);
    
        if (response.IsSuccess)
        {
            Console.WriteLine("Request was successful.");
        }
        else
        {
            Console.WriteLine("Request failed.");
        }
    }
}

Creating and Sending a Request with a Response

To create and send a request that returns a response of a specific type, you can define a request class and a response class.


using System.Net; 
using Xpandables.Net.Executions.Rests;

[RestGet("/api/data")] 
public sealed record GetDataRequest : IRestRequest<string>, IRestString;

public class SampleUsage 
{ 
    private readonly IRestClient _restClient;
    public SampleUsage(IRestClient restClient)
    {
        _restClient = restClient;
    }

    public async Task SendRequestWithResponseAsync()
    {
        var request = new GetDataRequest();
        RestResponse<string> response = await _restClient.SendAsync(request);
    
        if (response.IsSuccess)
        {
            Console.WriteLine($"Response data: {response.Result}");
        }
        else
        {
            Console.WriteLine("Request failed.");
        }
    }
}

To create and send a request that returns a response of stream type, you can define a request class and a response class.


using System.Net; 
using Xpandables.Net.Executions.Rests;

public sealed record Result(string Data);

[RestGet("/api/data")] 
public sealed record GetDataRequest : IRestRequestStream<Result>, IRestString;

public class SampleUsage 
{ 
    private readonly IRestClient _restClient;
    public SampleUsage(IRestClient restClient)
    {
        _restClient = restClient;
    }

    public async Task SendRequestWithResponseAsync()
    {
        var request = new GetDataRequest();
        RestResponse<IAsyncEnumerable<Result>> response = await _restClient.SendAsync(request);
        // response will be of type IAsyncEnumerable<Result>
        // You can use response.Result to access the stream of results.

        if (response.IsSuccess)
        {
            // iterate over the stream
        }
        else
        {
            Console.WriteLine("Request failed.");
        }
    }
}

Using a Custom Request Options Builder

To use a custom request options builder, implement the IRestAttributeBuilder interface in your request class.


using System.Net; 
using Xpandables.Net.Http;

public class CustomRequestAttributeBuilder : IRestAttributeBuilder 
{ 
    public RestAttribute Build(IServiceProvider serviceProvider) 
    { 
        return new RestAttribute 
            { 
                Path = "/api/custom", 
                Method = Method.POST, 
                ContentType = "application/json" 
            }; 
    } 
}

public sealed record CustomRequest : IRestRequest, CustomRequestAttributeBuilder;

public class SampleUsage 
{ 
    private readonly IRestClient _restClient;
    public SampleUsage(IRestClient restClient)
    {
        _restClient = restClient;
    }

    public async Task SendCustomRequestAsync()
    {
        var request = new CustomRequest();
        RestResponse response = await _restClient.SendAsync(request);
    
        if (response.IsSuccess)
        {
            Console.WriteLine("Custom request was successful.");
        }
        else
        {
            Console.WriteLine("Custom request failed.");
        }
    }
}

The IRestClient interface and related classes provide a flexible and structured way to handle HTTP client requests and responses in your application. By using these classes, you can ensure that your HTTP operations are consistent and detailed, making it easier to handle various HTTP scenarios.

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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (12)

Showing the top 5 NuGet packages that depend on Xpandables.Net:

Package Downloads
Xpandables.Net.DependencyInjection

A utility library in .Net7.0 to easily add dependency injection using the Xpandables.Net library

Xpandables.Net.EntityFramework

A utility library in .Net9.0 to easily add patterns such as CQRS

Xpandables.NetCore

A utility library in .Net5.0 to add AspNetCore behaviors to Xpandables.Net.

Xpandables.Net.AspNetCore

A utility library in .Net9.0 that adds AspNetCore behaviors to Xpandables.Net

Xpandables.Net.Windows

A utility library in .Net5.0 to extend Windows Forms and WPF behaviors.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
9.3.2 43 4/20/2025
9.3.0 189 4/14/2025
9.1.2 152 3/12/2025
9.1.0 170 3/8/2025
9.0.1 96 1/19/2025
9.0.0-rc.1 58 10/26/2024
8.1.2 145 9/12/2024
8.0.8 141 6/21/2024
8.0.6 119 5/25/2024
8.0.5 122 5/18/2024
8.0.1 309 2/11/2024
8.0.0 487 12/3/2023
8.0.0-rc.2.1.1 96 11/12/2023
8.0.0-rc.2.1 79 11/6/2023
8.0.0-rc.2.0 79 11/5/2023
7.3.3 628 5/9/2023
7.1.4 745 2/26/2023
7.1.3 822 2/19/2023
7.0.0 876 11/9/2022
7.0.0-rc2.0.1 123 10/12/2022
7.0.0-rc1.0.0 158 9/26/2022
6.1.1 1,384 8/6/2022
6.0.9 1,427 7/9/2022
6.0.8 1,406 6/27/2022
6.0.4 1,427 3/15/2022
6.0.3 1,410 2/22/2022
6.0.2 1,053 1/4/2022
6.0.1 1,084 12/4/2021
6.0.0 1,236 11/8/2021
6.0.0-rc.4.3 177 11/3/2021
6.0.0-rc.3.1 179 10/15/2021
6.0.0-rc.3 174 10/14/2021
6.0.0-rc.2 169 9/21/2021
6.0.0-preview.5 175 8/26/2021
5.6.1 1,566 6/30/2021
5.6.0 1,574 6/9/2021
5.5.1 1,548 5/26/2021
5.4.4 1,173 4/12/2021
5.4.0 998 3/11/2021
5.3.14 944 3/2/2021
5.3.13 934 2/25/2021
5.3.12 902 2/21/2021
5.3.11 911 2/18/2021
5.3.10 880 2/18/2021
5.3.9 1,000 2/11/2021
5.3.8 1,001 2/10/2021
5.3.7 915 2/7/2021
5.3.6 723 2/7/2021
5.3.5 918 2/7/2021
5.3.4 931 2/7/2021
5.3.3 930 2/5/2021
5.3.2 957 2/2/2021
5.3.1 969 1/31/2021
5.3.0 935 1/31/2021
5.2.14 962 1/26/2021
5.2.13 907 1/25/2021
5.2.12 954 1/22/2021
5.2.11 960 1/19/2021
5.2.10 921 1/16/2021
5.2.9 959 1/13/2021
5.2.8 961 1/8/2021
5.2.7 956 1/6/2021
5.2.6 945 1/6/2021
5.2.5 978 12/17/2020
5.2.4 918 12/12/2020
5.2.3 982 12/8/2020
5.2.2 974 12/7/2020
5.2.1 991 12/7/2020
5.2.0 1,130 12/6/2020
5.1.1 1,089 12/6/2020
5.1.0 970 12/5/2020
5.0.6 999 12/5/2020
5.0.5 952 11/23/2020
5.0.4 1,103 11/22/2020
5.0.3 1,227 11/20/2020
5.0.2 1,091 11/19/2020
5.0.1 1,452 11/16/2020
5.0.0 1,362 11/12/2020
5.0.0-rc.2.1.4 295 11/6/2020
5.0.0-rc.2.1.3 295 11/1/2020
5.0.0-rc.2.1.2 267 10/31/2020
5.0.0-rc.2.1.0 322 10/24/2020
5.0.0-rc.2.0.2 229 10/22/2020
5.0.0-rc.2.0.1 268 10/17/2020
5.0.0-rc.2.0.0 265 10/17/2020
5.0.0-rc.1.1.5 336 10/11/2020
5.0.0-rc.1.1.4 327 10/11/2020
5.0.0-rc.1.1.3 376 10/10/2020
5.0.0-rc.1.1.2 256 10/4/2020
5.0.0-rc.1.1.1 283 10/2/2020
5.0.0-rc.1.1.0 249 10/1/2020
5.0.0-rc.1.0.9 307 9/29/2020
5.0.0-rc.1.0.8 237 9/28/2020
5.0.0-rc.1.0.7 278 9/28/2020
5.0.0-rc.1.0.6 334 9/26/2020
5.0.0-rc.1.0.5 288 9/25/2020
5.0.0-rc.1.0.4 248 9/24/2020
5.0.0-rc.1.0.3 314 9/23/2020
5.0.0-rc.1.0.2 273 9/23/2020
5.0.0-rc.1.0.1 255 9/21/2020
5.0.0-preview.2.0.0 268 8/16/2020
5.0.0-preview.1.0.8 261 8/10/2020
5.0.0-preview.1.0.7 320 8/10/2020
5.0.0-preview.1.0.6 324 8/5/2020
5.0.0-preview.1.0.5 310 8/3/2020
5.0.0-preview.1.0.4 272 8/3/2020
5.0.0-preview.1.0.3 327 8/2/2020
5.0.0-preview.1.0.2 359 8/2/2020
5.0.0-preview.1.0.1 376 8/2/2020
5.0.0-preview.1.0.0 419 7/25/2020
3.2.1 1,398 9/17/2020
3.2.0 1,416 9/15/2020
3.1.9 1,389 9/13/2020
3.1.8 1,475 9/13/2020
3.1.7 1,396 9/12/2020
3.1.6 1,568 9/5/2020
3.1.5 1,399 9/4/2020
3.1.4 1,508 8/29/2020
3.1.3 1,367 8/28/2020
3.1.2 1,388 8/27/2020
3.1.1 1,404 8/24/2020
3.1.0 1,434 8/19/2020

Fix IPipelineDecorator implementation