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 (18)

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 (1)

Showing the top 1 popular GitHub repositories that depend on ManagedCode.Communication:

Repository Stars
managedcode/Storage
Storage library provides a universal interface for accessing and manipulating data in different cloud blob storage providers
Version Downloads Last Updated
10.0.1 280 2/7/2026
10.0.0 3,285 11/12/2025
9.6.5 2,198 9/24/2025
9.6.4 3,596 9/20/2025
9.6.3 255 9/20/2025
9.6.2 355 8/22/2025
9.6.1 461 8/13/2025
9.6.0 929 8/12/2025
9.5.2 356 8/5/2025
9.5.1 310 8/5/2025
9.5.0 325 8/5/2025
9.0.1 264 8/4/2025
9.0.0 8,237 12/10/2024
8.0.7 8,696 9/12/2024
8.0.6 1,452 6/29/2024
8.0.5 320 6/27/2024
8.0.4 314 6/27/2024
8.0.3 4,337 6/14/2024
8.0.2 586 6/13/2024
8.0.1 527 5/6/2024
Loading failed