Sisusa.ServiceExceptions
1.0.0
dotnet add package Sisusa.ServiceExceptions --version 1.0.0
NuGet\Install-Package Sisusa.ServiceExceptions -Version 1.0.0
<PackageReference Include="Sisusa.ServiceExceptions" Version="1.0.0" />
<PackageVersion Include="Sisusa.ServiceExceptions" Version="1.0.0" />
<PackageReference Include="Sisusa.ServiceExceptions" />
paket add Sisusa.ServiceExceptions --version 1.0.0
#r "nuget: Sisusa.ServiceExceptions, 1.0.0"
#:package Sisusa.ServiceExceptions@1.0.0
#addin nuget:?package=Sisusa.ServiceExceptions&version=1.0.0
#tool nuget:?package=Sisusa.ServiceExceptions&version=1.0.0
Service Exceptions Documentation
This document provides comprehensive usage examples and best practices for the Sisusa.ServiceExceptions
namespace, which contains specialized exception classes for service-layer operations.
Table of Contents
- ServiceException
- AccessDeniedException
- AuthenticationException
- ConcurrencyException
- ConfigurationException
- DuplicateEntityException
- EntityNotFoundException
- SecurityException
- Advanced Usage Patterns
- Best Practices
ServiceException
Base class for all service exceptions. Provides common functionality like error codes.
Basic Usage
throw new ServiceException("Failed to process the request");
With Error Code
throw new ServiceException("Failed to process the request")
{
ErrorCode = "PROCESSING_ERROR"
};
Real-world Example
try
{
// Service operation
}
catch (Exception ex)
{
throw new ServiceException("Order processing failed", ex);
}
AuthenticationException
Thrown when authentication fails.
Basic Usage
if (!user.IsAuthenticated)
{
throw new AuthenticationException("Invalid credentials");
}
Real-world Example
public async Task<User> AuthenticateAsync(string username, string password)
{
var user = await _userRepository.GetByUsernameAsync(username);
if (user == null || !VerifyPassword(user.PasswordHash, password))
{
throw new AuthenticationException("Invalid username or password");
}
return user;
}
ConcurrencyException
Thrown when a concurrency conflict occurs (e.g., optimistic concurrency).
Basic Usage
throw new ConcurrencyException(entity, "Record was modified by another user");
Real-world Example
public async Task UpdateOrderAsync(Order order)
{
var existing = await _orderRepository.GetByIdAsync(order.Id);
if (existing.Version != order.Version)
{
throw new ConcurrencyException(order,
"Order was modified by another process. Please refresh and try again.");
}
await _orderRepository.UpdateAsync(order);
}
ConfigurationException
Thrown for configuration-related issues.
Basic Usage
var apiKey = Configuration["ApiKey"]
?? throw new ConfigurationException("ApiKey", "API key is required");
Using Builder Pattern
throw ConfigurationExceptionBuilder.For("DatabaseConnection")
.WithMessage("Failed to establish database connection")
.WithInnerException(dbEx)
.WithErrorCode("DB_CONN_FAILURE")
.Build();
Real-world Example
public string GetRequiredConfig(string key)
{
try
{
return _configuration[key]
?? throw new ConfigurationException(key, $"Configuration '{key}' is missing");
}
catch (Exception ex)
{
throw ConfigurationExceptionBuilder.For(key)
.WithMessage($"Failed to read configuration '{key}'")
.WithInnerException(ex)
.Build();
}
}
DuplicateEntityException
Thrown when a duplicate entity is detected.
Basic Usage
if (await _userRepository.ExistsAsync(email))
{
throw new DuplicateEntityException($"User with email {email} already exists");
}
Using Helper Methods
var existingUser = await _userRepository.GetByEmailAsync(email);
DuplicateEntityException.ThrowIfExists(existingUser, $"Email {email} is already registered");
Real-world Example
public async Task CreateUserAsync(User newUser)
{
// Check if email exists
var existing = await _userRepository.GetByEmailAsync(newUser.Email);
DuplicateEntityException.ThrowIfExists(existing,
$"User with email {newUser.Email} already exists");
// Check if username exists with additional condition
var usernameExists = await _userRepository.GetByUsernameAsync(newUser.Username);
DuplicateEntityException.ThrowIfExists(usernameExists,
u => !u.IsDeleted,
$"Username {newUser.Username} is taken");
await _userRepository.AddAsync(newUser);
}
EntityNotFoundException
Thrown when an expected entity is not found.
Basic Usage
var user = await _userRepository.GetByIdAsync(id)
?? throw new EntityNotFoundException($"User {id} not found");
Using Helper Methods
var order = await _orderRepository.GetByIdAsync(orderId);
EntityNotFoundException.ThrowIfNull(order, $"Order {orderId} not found");
Real-world Example
public async Task<Order> GetOrderDetailsAsync(int orderId, int userId)
{
var order = await _orderRepository.GetByIdAsync(orderId);
EntityNotFoundException.ThrowIfNull(order, $"Order {orderId} not found");
// Additional validation
EntityNotFoundException.ThrowIfNull(order,
o => o.UserId == userId,
$"Order {orderId} not found for user {userId}");
return order;
}
SecurityException
Base class for security-related exceptions.
Basic Usage
if (!IsOperationAllowed(user))
{
throw new SecurityException("Operation not permitted due to security constraints");
}
Real-world Example
public void ProcessPayment(Payment payment)
{
if (payment.Amount > _userService.GetMaxPaymentAmount())
{
throw new SecurityException(
$"Payment amount {payment.Amount} exceeds maximum allowed");
}
// Process payment
}
AccessDeniedException
Thrown when authorization fails(user is logged in but not permitted to do the operation) and the operation has to be halted.
Basic Usage
var requiredPermission = "EDIT_ORDERS";
AccessDeniedException.ThrowIf(u=> !u.HasPermission(requiredPermission), user, requiredPermission);
//or if you prefer to be more verbose:
if (!user.HasPermission("EDIT_ORDERS"))
{
throw new AccessDeniedException("EDIT_ORDERS");
}
Real-world Example
public void DeleteUser(int userId, User requestingUser)
{
if (!requestingUser.IsAdmin)
{
throw new AccessDeniedException("DELETE_USERS");
}
_userRepository.Delete(userId);
}
Advanced Usage Patterns
1. Exception Translation Middleware
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
switch (exception)
{
case EntityNotFoundException ex:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
await context.Response.WriteAsJsonAsync(new { ex.Message });
break;
case AccessDeniedException ex:
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await context.Response.WriteAsJsonAsync(new { ex.Message, ex.RequiresPermission });
break;
// Handle other exception types...
}
});
});
2. Domain-Specific Exception Subclasses
public class OrderProcessingException : ServiceException
{
public int OrderId { get; }
public OrderProcessingException(int orderId, string message)
: base(message)
{
OrderId = orderId;
ErrorCode = "ORDER_ERROR";
}
}
// Usage:
throw new OrderProcessingException(order.Id, "Payment processing failed");
3. Exception Enrichment
try
{
// Service operation
}
catch (Exception ex) when (ex is not ServiceException)
{
var enrichedEx = new ServiceException("Operation failed", ex)
{
ErrorCode = "OPERATION_FAILED",
Data = {
["Timestamp"] = DateTime.UtcNow,
["UserId"] = currentUserId
}
};
throw enrichedEx;
}
Best Practices
Use Specific Exceptions: Always use the most specific exception type that fits the error condition.
Provide Context: Include relevant information in exception messages:
// Good throw new EntityNotFoundException($"Product with ID {productId} not found"); // Bad throw new EntityNotFoundException("Not found");
Use Helper Methods: Leverage the static helper methods for common checks:
EntityNotFoundException.ThrowIfNull(product); DuplicateEntityException.ThrowIfExists(existingUser);
Preserve Stack Traces: Always include inner exceptions when wrapping exceptions:
catch (DbException ex) { throw new ServiceException("Database operation failed", ex); }
Standardize Error Codes: Define a standard set of error codes for your application.
Log Before Throwing: Consider logging exceptions before throwing them:
_logger.LogError(ex, "Failed to process order {OrderId}", orderId); throw new OrderProcessingException(orderId, "Processing failed", ex);
Document Exception Contracts: Document which exceptions your methods can throw.
Use Builder Pattern for Complex Exceptions: For exceptions with many properties (like
ConfigurationException
), use the builder pattern for cleaner code.Consider Localization: For user-facing messages, consider adding support for localized messages.
Test Exception Cases: Write unit tests that verify your exceptions are thrown in the right circumstances.
Product | Versions 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. |
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
1.0.0 | 114 | 6/7/2025 |
Initial Release