فرض کن اینترفیس های زیر رو داریم
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;
public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpPost]
public async Task<IActionResult> PlaceOrder([FromBody] PlaceOrderRequest request)
{
if (request == null || request.ProductId <= 0 || request.Quantity <= 0 || string.IsNullOrWhiteSpace(request.CustomerEmail))
{
return BadRequest("Invalid order details provided.");
}
var success = await _orderService.PlaceOrderAsync(request.ProductId, request.Quantity, request.CustomerEmail);
if (success)
{
return Ok(new { Message = "Order placed successfully!" });
}
return StatusCode(500, new { Message = "Failed to place order. Please try again." });
}
[HttpGet("{orderId}")]
public async Task<IActionResult> GetOrderDetails(int orderId)
{
var order = await _orderService.GetOrderDetailsAsync(orderId);
if (order == null)
{
return NotFound($"Order with ID {orderId} not found.");
}
return Ok(order);
}
}
public interface IOrderService
{
Task<bool> PlaceOrderAsync(int productId, int quantity, string customerEmail);
Task<Order?> GetOrderDetailsAsync(int orderId);
}
public class OrderService : IOrderService
{
private int _nextOrderId = 1;
private readonly List<Order> _orders = new List<Order>();
public async Task<bool> PlaceOrderAsync(int productId, int quantity, string customerEmail)
{
await Task.Delay(200); // Simulate some processing time
var order = new Order
{
Id = _nextOrderId++,
ProductId = productId,
Quantity = quantity,
CustomerEmail = customerEmail
};
_orders.Add(order);
Console.WriteLine($"[OrderService]: Order placed for ProductId: {productId}, Quantity: {quantity} by {customerEmail}. Order ID: {order.Id}");
return true;
}
public async Task<Order?> GetOrderDetailsAsync(int orderId)
{
await Task.Delay(150); // Simulate fetching time
Console.WriteLine($"[OrderService]: Fetching details for Order ID: {orderId}.");
return _orders.FirstOrDefault(o => o.Id == orderId);
}
}
Decorators
public class CachedOrderService : IOrderService
{
private readonly IOrderService _decoratedOrderService;
private readonly IMemoryCache _memoryCache;
private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(10);
public CachedOrderService(IOrderService decoratedOrderService, IMemoryCache memoryCache)
{
_decoratedOrderService = decoratedOrderService;
_memoryCache = memoryCache;
}
public async Task<bool> PlaceOrderAsync(int productId, int quantity, string customerEmail)
{
Console.WriteLine("[CachedOrderService]: PlaceOrder bypasses cache. Passing to next decorator...");
return await _decoratedOrderService.PlaceOrderAsync(productId, quantity, customerEmail);
}
public async Task<Order?> GetOrderDetailsAsync(int orderId)
{
string cacheKey = $"Order_{orderId}";
if (_memoryCache.TryGetValue(cacheKey, out Order? order))
{
Console.WriteLine($"[CachedOrderService]: Cache HIT for Order ID {orderId}.");
return order;
}
else
{
Console.WriteLine($"[CachedOrderService]: Cache MISS for Order ID {orderId}. Fetching from decorated service...");
order = await _decoratedOrderService.GetOrderDetailsAsync(orderId);
if (order != null)
{
_memoryCache.Set(cacheKey, order, CacheDuration);
}
return order;
}
}
}
public class LoggingOrderService : IOrderService
{
private readonly IOrderService _decoratedOrderService;
private readonly ILogger<LoggingOrderService> _logger;
public LoggingOrderService(IOrderService decoratedOrderService, ILogger<LoggingOrderService> logger)
{
_decoratedOrderService = decoratedOrderService;
_logger = logger;
}
public async Task<bool> PlaceOrderAsync(int productId, int quantity, string customerEmail)
{
_logger.LogInformation("Attempting to place order for ProductId: {ProductId}, Quantity: {Quantity}, Email: {CustomerEmail}",
productId, quantity, customerEmail);
Console.WriteLine("[LoggingOrderService]: Logging order placement attempt. Passing to next decorator...");
var result = await _decoratedOrderService.PlaceOrderAsync(productId, quantity, customerEmail);
_logger.LogInformation("Order placement for ProductId: {ProductId} {(Result ? succeeded : failed)}.", productId, result);
Console.WriteLine($"[LoggingOrderService]: Logging order placement result: {result}.");
return result;
}
public async Task<Order?> GetOrderDetailsAsync(int orderId)
{
_logger.LogInformation("Requesting order details for Order ID: {OrderId}", orderId);
Console.WriteLine($"[LoggingOrderService]: Logging order details request for ID {orderId}. Passing to next decorator...");
var order = await _decoratedOrderService.GetOrderDetailsAsync(orderId);
if (order == null)
{
_logger.LogWarning("Order with ID {OrderId} not found.", orderId);
}
else
{
_logger.LogInformation("Successfully retrieved details for Order ID: {OrderId}", orderId);
}
Console.WriteLine($"[LoggingOrderService]: Logging order details retrieval result: {(order != null ? "Found" : "Not Found")}.");
return order;
}
}
public class ValidatingOrderService : IOrderService
{
private readonly IOrderService _decoratedOrderService;
public ValidatingOrderService(IOrderService decoratedOrderService)
{
_decoratedOrderService = decoratedOrderService;
}
public async Task<bool> PlaceOrderAsync(int productId, int quantity, string customerEmail)
{
if (productId <= 0 || quantity <= 0 || string.IsNullOrWhiteSpace(customerEmail) || !customerEmail.Contains("@"))
{
Console.WriteLine("[ValidatingOrderService]: Validation Error: Invalid order details (ProductId, Quantity, or CustomerEmail).");
return false;
}
Console.WriteLine("[ValidatingOrderService]: Order details validated successfully. Passing to next decorator...");
return await _decoratedOrderService.PlaceOrderAsync(productId, quantity, customerEmail);
}
public async Task<Order?> GetOrderDetailsAsync(int orderId)
{
if (orderId <= 0)
{
Console.WriteLine("[ValidatingOrderService]: Validation Error: Invalid Order ID.");
return null;
}
Console.WriteLine("[ValidatingOrderService]: Order ID validated successfully. Passing to next decorator...");
return await _decoratedOrderService.GetOrderDetailsAsync(orderId);
}
}
Install Scrutor
<PackageReference Include="Scrutor" Version="6.1.0" />
builder.Services.Scan(scan => scan
.FromAssemblyOf<Program>() // از اسمبلی جاری اسکن میکنیم
.AddClasses(classes => classes.AssignableTo<IOrderService>())
.AsImplementedInterfaces()
.WithScopedLifetime());
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, CachedOrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderService>();
builder.Services.Decorate<IOrderService, ValidatingOrderService>();
فرض کن اینترفیس های زیر رو داریم
Decorators
Install Scrutor