ManagedCode.Communication 9.5.0

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

ManagedCode.Communication

.NET Coverage Status nuget CodeQL NuGet Package NuGet Downloads

A powerful .NET library that revolutionizes error handling by providing a Result pattern implementation, eliminating exceptions and making your code more predictable, testable, and maintainable.

๐ŸŽฏ Why ManagedCode.Communication?

Traditional exception-based error handling can make code difficult to follow and test. The Communication library introduces a Result pattern that transforms how you handle operations that might fail:

  • โœ… No More Exceptions - Replace try-catch blocks with elegant Result objects
  • ๐Ÿ” Explicit Error Handling - Makes potential failures visible in method signatures
  • ๐Ÿงช Better Testability - No need to test exception scenarios
  • ๐Ÿš€ Improved Performance - Avoid the overhead of throwing exceptions
  • ๐Ÿ“ Self-Documenting Code - Method signatures clearly indicate possible failures

๐Ÿ“ฆ Installation

# Core library
dotnet add package ManagedCode.Communication

# ASP.NET Core integration
dotnet add package ManagedCode.Communication.Extensions

# Orleans integration
dotnet add package ManagedCode.Communication.Orleans

๐Ÿš€ Quick Start

Basic Usage

using ManagedCode.Communication;

// Simple success result
var success = Result.Succeed();
if (success.IsSuccess)
{
    Console.WriteLine("Operation succeeded!");
}

// Simple failure result
var failure = Result.Fail("Something went wrong");
if (failure.IsFailed)
{
    Console.WriteLine($"Error: {failure.GetError()}");
}

Generic Results with Values

// Success with value
var userResult = Result<User>.Succeed(new User { Id = 1, Name = "John" });
if (userResult.IsSuccess)
{
    var user = userResult.Value; // Access the user object
    Console.WriteLine($"Found user: {user.Name}");
}

// Failure with error details
var notFound = Result<User>.Fail("User not found", HttpStatusCode.NotFound);
if (notFound.IsFailed)
{
    Console.WriteLine($"Error: {notFound.GetError()} (Status: {notFound.StatusCode})");
}

Collection Results

Perfect for paginated API responses:

var products = await GetProductsAsync(page: 1, pageSize: 20);

var result = CollectionResult<Product>.Succeed(
    items: products,
    page: 1,
    pageSize: 20,
    totalCount: 150
);

// Access pagination info
Console.WriteLine($"Page {result.Page} of {result.TotalPages}");
Console.WriteLine($"Showing {result.Items.Count()} of {result.TotalCount} products");

Async Operations with Result.From

Convert any operation into a Result:

// Wrap synchronous operations
var result = await Result<string>.From(() => 
{
    return File.ReadAllText("config.json");
});

// Wrap async operations
var apiResult = await Result<WeatherData>.From(async () => 
{
    return await weatherService.GetCurrentWeatherAsync("London");
});

if (apiResult.IsSuccess)
{
    Console.WriteLine($"Temperature: {apiResult.Value.Temperature}ยฐC");
}
else
{
    Console.WriteLine($"API call failed: {apiResult.GetError()}");
}

๐ŸŒ ASP.NET Core Integration

Configure Services

using ManagedCode.Communication.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add Communication services
builder.Services.AddCommunication(options =>
{
    options.ShowErrorDetails = builder.Environment.IsDevelopment();
});

// Add MVC with Communication filters
builder.Services.AddControllers(options =>
{
    options.AddCommunicationFilters();
});

// Add SignalR with Communication filters
builder.Services.AddSignalR(options => 
{
    options.AddCommunicationFilters();
});

var app = builder.Build();

// Use Communication middleware for global error handling
app.UseCommunication();

Controller Examples

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet("{id}")]
    public async Task<Result<UserDto>> GetUser(int id)
    {
        var user = await _userService.GetByIdAsync(id);
        
        if (user == null)
            return Result<UserDto>.Fail($"User with ID {id} not found", HttpStatusCode.NotFound);
            
        return Result<UserDto>.Succeed(user.ToDto());
    }

    [HttpPost]
    public async Task<Result<UserDto>> CreateUser([FromBody] CreateUserDto dto)
    {
        // Model validation is handled automatically by CommunicationModelValidationFilter
        var user = await _userService.CreateAsync(dto);
        return Result<UserDto>.Succeed(user.ToDto(), HttpStatusCode.Created);
    }

    [HttpGet]
    public async Task<CollectionResult<UserDto>> GetUsers([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
    {
        var (users, totalCount) = await _userService.GetPagedAsync(page, pageSize);
        
        return CollectionResult<UserDto>.Succeed(
            users.Select(u => u.ToDto()),
            page,
            pageSize,
            totalCount
        );
    }
}

SignalR Hub Example

public class NotificationHub : Hub
{
    private readonly INotificationService _notificationService;

    public NotificationHub(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public async Task<Result> SendNotification(string message)
    {
        if (string.IsNullOrWhiteSpace(message))
            return Result.Fail("Message cannot be empty");

        await _notificationService.BroadcastAsync(message);
        return Result.Succeed();
    }

    public async Task<Result<int>> GetUnreadCount()
    {
        var count = await _notificationService.GetUnreadCountAsync(Context.UserIdentifier);
        return Result<int>.Succeed(count);
    }
}

๐ŸŽจ Advanced Features

Custom Error Types

public class ValidationError : Error
{
    public Dictionary<string, string[]> Errors { get; }
    
    public ValidationError(Dictionary<string, string[]> errors) 
        : base("Validation failed", HttpStatusCode.BadRequest)
    {
        Errors = errors;
    }
}

// Usage
var validationErrors = new Dictionary<string, string[]>
{
    ["Email"] = ["Invalid email format", "Email already exists"],
    ["Password"] = ["Password must be at least 8 characters"]
};

return Result<User>.Fail(new ValidationError(validationErrors));

Result Extensions and Chaining

// Map successful results
var result = await GetUserAsync(id)
    .Map(user => user.ToDto())
    .Map(dto => new UserViewModel(dto));

// Handle both success and failure cases
var message = await CreateOrderAsync(orderDto)
    .Match(
        onSuccess: order => $"Order {order.Id} created successfully",
        onFailure: error => $"Failed to create order: {error.Message}"
    );

// Chain operations
var finalResult = await GetUserAsync(userId)
    .Bind(user => ValidateUserAsync(user))
    .Bind(user => CreateOrderForUserAsync(user, orderDto))
    .Map(order => order.ToDto());

Global Exception Handling

The Communication filters automatically convert exceptions to Result objects:

// This exception will be caught and converted to Result.Fail
[HttpGet("{id}")]
public async Task<Result<Product>> GetProduct(int id)
{
    // If this throws, CommunicationExceptionFilter handles it
    var product = await _repository.GetByIdAsync(id);
    return Result<Product>.Succeed(product);
}

Status Code Mapping

The library automatically maps exceptions to appropriate HTTP status codes:

// Built-in mappings
ArgumentException           โ†’ 400 Bad Request
UnauthorizedAccessException โ†’ 401 Unauthorized  
KeyNotFoundException       โ†’ 404 Not Found
InvalidOperationException   โ†’ 409 Conflict
NotImplementedException     โ†’ 501 Not Implemented

// ASP.NET Core specific
BadHttpRequestException     โ†’ 400 Bad Request
AuthenticationFailureException โ†’ 401 Unauthorized
AntiforgeryValidationException โ†’ 400 Bad Request

๐Ÿ—๏ธ Orleans Integration

// Silo configuration
var builder = new HostBuilder()
    .UseOrleans(siloBuilder =>
    {
        siloBuilder.UseOrleansCommunication();
    });

// Client configuration
var client = new ClientBuilder()
    .UseOrleansCommunication()
    .Build();

// Grain implementation
public class UserGrain : Grain, IUserGrain
{
    public Task<Result<UserData>> GetUserDataAsync()
    {
        try
        {
            var userData = LoadUserData();
            return Task.FromResult(Result<UserData>.Succeed(userData));
        }
        catch (Exception ex)
        {
            return Task.FromResult(Result<UserData>.Fail(ex));
        }
    }
}

๐Ÿ“Š Performance Benefits

Using Result pattern instead of exceptions provides significant performance improvements:

// โŒ Traditional approach - throwing exceptions
public User GetUser(int id)
{
    var user = _repository.FindById(id);
    if (user == null)
        throw new NotFoundException($"User {id} not found"); // Expensive!
    return user;
}

// โœ… Result pattern - no exceptions
public Result<User> GetUser(int id)
{
    var user = _repository.FindById(id);
    if (user == null)
        return Result<User>.Fail($"User {id} not found"); // Much faster!
    return Result<User>.Succeed(user);
}

๐Ÿงช Testing

Result pattern makes testing much cleaner:

[Test]
public async Task GetUser_WhenUserExists_ReturnsSuccess()
{
    // Arrange
    var userId = 123;
    var expectedUser = new User { Id = userId, Name = "John" };
    _mockRepository.Setup(x => x.FindById(userId)).Returns(expectedUser);

    // Act
    var result = await _userService.GetUser(userId);

    // Assert
    Assert.IsTrue(result.IsSuccess);
    Assert.AreEqual(expectedUser.Name, result.Value.Name);
}

[Test]
public async Task GetUser_WhenUserNotFound_ReturnsFailure()
{
    // Arrange
    var userId = 999;
    _mockRepository.Setup(x => x.FindById(userId)).Returns((User)null);

    // Act
    var result = await _userService.GetUser(userId);

    // Assert
    Assert.IsFalse(result.IsSuccess);
    Assert.AreEqual(HttpStatusCode.NotFound, result.StatusCode);
}

๐Ÿ› ๏ธ Configuration Options

services.AddCommunication(options =>
{
    // Show detailed error information (disable in production)
    options.ShowErrorDetails = false;
    
    // Custom error response builder
    options.ErrorResponseBuilder = (error, context) =>
    {
        return new
        {
            error = error.Message,
            timestamp = DateTime.UtcNow,
            path = context.Request.Path
        };
    };
    
    // Custom status code mapping
    options.StatusCodeMapping[typeof(CustomException)] = HttpStatusCode.Conflict;
});

๐Ÿ“ Best Practices

  1. Always return Result types from your service methods

    public interface IUserService
    {
        Task<Result<User>> GetByIdAsync(int id);
        Task<Result<User>> CreateAsync(CreateUserDto dto);
        Task<Result> DeleteAsync(int id);
    }
    
  2. Use specific error messages and appropriate status codes

    return Result<Order>.Fail(
        "Insufficient inventory for product SKU-123", 
        HttpStatusCode.UnprocessableEntity
    );
    
  3. Leverage pattern matching for elegant error handling

    var response = await ProcessOrder(orderId) switch
    {
        { IsSuccess: true } result => Ok(result.Value),
        { StatusCode: HttpStatusCode.NotFound } => NotFound(),
        var failure => BadRequest(failure.GetError())
    };
    

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments

Special thanks to all contributors who have helped make this library better!


Made with โค๏ธ by ManagedCode

Product 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 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 (14)

Showing the top 5 NuGet packages that depend on ManagedCode.Communication:

Package Downloads
ManagedCode.Storage.Core

Base interfaces for ManagedCode.StorageS

ManagedCode.Storage.Azure

Storage for Azure

ManagedCode.Communication.Orleans

Communication for .NET

ManagedCode.Storage.Aws

Storage for AWS

ManagedCode.Storage.FileSystem

Storage for FileSystem

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
9.6.1 0 8/13/2025
9.6.0 10 8/12/2025
9.5.2 169 8/5/2025
9.5.1 174 8/5/2025
9.5.0 168 8/5/2025
9.0.1 141 8/4/2025
9.0.0 5,348 12/10/2024
8.0.7 3,202 9/12/2024
8.0.6 1,325 6/29/2024
8.0.5 188 6/27/2024
8.0.4 188 6/27/2024
8.0.3 3,805 6/14/2024
8.0.2 381 6/13/2024
8.0.1 362 5/6/2024
8.0.0 4,229 11/29/2023
2.0.26 5,331 7/12/2023
2.0.25 1,337 6/6/2023
2.0.24 39,593 5/22/2023
2.0.23 3,844 5/21/2023
2.0.22 280 5/17/2023
2.0.21 287 5/17/2023
2.0.20 246 5/17/2023
2.0.19 9,173 3/15/2023
2.0.18 405 3/14/2023
2.0.17 356 3/12/2023
2.0.16 481 2/21/2023
2.0.15 791 2/17/2023
2.0.14 879 2/7/2023
2.0.13 7,961 12/19/2022
2.0.12 440 12/19/2022
2.0.11 477 12/8/2022
2.0.10 539 12/3/2022
2.0.9 394 11/24/2022
2.0.8 405 11/24/2022
2.0.7 419 11/24/2022
2.0.5 414 11/24/2022
2.0.3 405 11/24/2022
2.0.2 408 11/24/2022
2.0.1 427 11/24/2022
2.0.0 418 11/22/2022
1.0.3 2,609 11/1/2022
1.0.2 2,274 10/27/2022
1.0.1 475 10/26/2022
1.0.0 8,122 10/17/2022
0.1.2 13,425 8/11/2022
0.1.1 493 8/11/2022
0.1.0 483 8/11/2022
0.0.8 8,761 7/27/2022
0.0.7 498 7/27/2022
0.0.6 7,239 7/19/2022
0.0.5 521 7/19/2022
0.0.4 483 7/19/2022
0.0.3 2,293 7/16/2022
0.0.2 513 7/15/2022
0.0.1 510 7/15/2022