ManagedCode.Communication
9.5.0
Prefix Reserved
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
<PackageReference Include="ManagedCode.Communication" Version="9.5.0" />
<PackageVersion Include="ManagedCode.Communication" Version="9.5.0" />
<PackageReference Include="ManagedCode.Communication" />
paket add ManagedCode.Communication --version 9.5.0
#r "nuget: ManagedCode.Communication, 9.5.0"
#:package ManagedCode.Communication@9.5.0
#addin nuget:?package=ManagedCode.Communication&version=9.5.0
#tool nuget:?package=ManagedCode.Communication&version=9.5.0
ManagedCode.Communication
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
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); }
Use specific error messages and appropriate status codes
return Result<Order>.Fail( "Insufficient inventory for product SKU-123", HttpStatusCode.UnprocessableEntity );
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 | Versions 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. |
-
net8.0
- Microsoft.Extensions.Logging (>= 9.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.7)
-
net9.0
- Microsoft.Extensions.Logging (>= 9.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.7)
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 |