Skip to content

Decorator Pattern #4

Description

@mehransattari

فرض کن اینترفیس های زیر رو داریم

Image
[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>();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions