The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one, and make them interchangeable.
In simpler words: instead of writing if-else or switch statements to select a behavior, you can swap the behavior dynamically at runtime without touching the client code.
This blog demonstrates a real-world example of the Strategy Pattern in C# / ASP.NET Core, using a logging system. We will dynamically choose between Console, File, and Database logging strategies via a web API.
By the end of this tutorial, you’ll see how to:
Use Strategy Pattern to decouple logging logic
Dynamically select a logging strategy at runtime
Build a scalable, maintainable backend API
The Strategy Pattern is useful in situations where behavior varies dynamically. Some common use cases include:
Logging: Console, File, Database, Email
Payment processing: Stripe, PayPal, Razorpay
File export: CSV, TXT, JSON, PDF
Sorting / Filtering: Dashboards and reports
Authentication / Authorization: Multiple providers or methods
In our POC, we focus on logging, which is a universal requirement in almost all backend systems.
The first step is to create a common interface for all logging strategies:namespace StrategyPattern.API.Strategies;
public interface ILoggerStrategyServcie{ public string Name { get; } public Task LogAsync(string message);}
Name identifies the strategy (used for runtime selection)
LogAsync defines the logging behavior (async-friendly for real-world use)
Console Logger
public class ConsoleLoggerService : ILoggerStrategyServcie{ public string Name => "console";
public Task LogAsync(string message) { if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message));
Console.WriteLine($"[{DateTime.UtcNow}]{message}"); return Task.CompletedTask; }}
File Logger
public class FileLoggerService : ILoggerStrategyServcie{ public string Name => "file"; private readonly string _filePath = "logs.txt";
public Task LogAsync(string message) { if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message)); File.AppendAllText(_filePath, $"[{DateTime.UtcNow}] {message}" + Environment.NewLine); return Task.CompletedTask; }}
Database Logger (Simulated)
In production, DatabaseLoggerService would save logs to a database using EF Core or ADO.NET.
public class DatabaseLoggerService : ILoggerStrategyServcie{ public string Name => "db";
public Task LogAsync(string message) { if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message)); Console.WriteLine($"[DB][{DateTime.UtcNow}] {message}"); return Task.CompletedTask; }}
The Logger Resolver dynamically selects a logger based on a string key:
public class LoggerResolver{ private readonly IEnumerable<ILoggerStrategyServcie> _loggers;
public LoggerResolver(IEnumerable<ILoggerStrategyServcie> loggers) { _loggers = loggers ?? throw new ArgumentNullException(nameof(loggers)); }
public ILoggerStrategyServcie Resolve(string name) { var logger = _loggers.FirstOrDefault(l => l.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); return logger ?? throw new NotSupportedException($"Logger '{name}' not supported"); }}
Step 4: Create the Web API Controller[Route("api/[controller]")][ApiController]public class LogController : ControllerBase{ private readonly LoggerResolver _resolver;
public LogController(LoggerResolver resolver) { _resolver = resolver; }
[HttpPost] public async Task<IActionResult> Log(string message, string logger) { var strategy = _resolver.Resolve(logger); await strategy.LogAsync(message); return Ok("Logged successfully"); }}
Step 5: Configure Dependency Injection and Swagger
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// Register logging strategiesbuilder.Services.AddTransient<ILoggerStrategyServcie, ConsoleLoggerService>();builder.Services.AddTransient<ILoggerStrategyServcie, FileLoggerService>();builder.Services.AddTransient<ILoggerStrategyServcie, DatabaseLoggerService>();
// Register the resolverbuilder.Services.AddSingleton<LoggerResolver>();
// Swaggerbuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Strategy Pattern Logging API V1") );}
app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();
Step 6: Test the Logging API
POST /api/log?logger=console&message=HelloWorld
POST /api/log?logger=file&message=File log test
POST /api/log?logger=db&message=Database log test
Open/Closed Principle: Add new logging strategies without changing controller or resolver
Dependency Injection: Decouples logger creation from usage
Runtime Strategy Selection: Easily switch loggers dynamically
Extensible: Add EmailLogger, CloudLogger, or third-party logging in minutes
Add LogEntry with LogLevel, Source, CorrelationId for structured logging
Integrate real database logging (EF Core or ADO.NET)
Add async file or cloud logging
Include fallback strategies if a logger fails
The Strategy Pattern is a powerful tool for dynamic behavior selection in real-world apps.
This POC demonstrates:
How to structure logging with multiple strategies
How to make your code clean, scalable, and maintainable
How to apply design patterns in a web API context
By using Strategy Pattern, you no longer need messy if-else chains, and adding new loggers is trivial — perfect for enterprise systems.
I’ve shared this code in a GitHub repo:
Repo name and Link: StrategyPatternLogging
Thanks, for reading the blog, I hope it helps you. Please share this link on your social media accounts so that others can read our valuable content. Share your queries with our expert team and get Free Expert Advice for Your Business today.
Hire me on Linkedin
My portfolio