Real-World Strategy Pattern in C# – Logging Example with Console, File, and Database

Real-World Strategy Pattern in C# – Logging Example with Console, File, and Database

Introduction

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


When to Use the Strategy Pattern

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.


Step 1: Define the Strategy Interface

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)

Step 2: Implement Concrete Logging Strategies

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;
    }
}

Step 3: Create the Logger Resolver (Factory)

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 strategies
builder.Services.AddTransient<ILoggerStrategyServcie, ConsoleLoggerService>();
builder.Services.AddTransient<ILoggerStrategyServcie, FileLoggerService>();
builder.Services.AddTransient<ILoggerStrategyServcie, DatabaseLoggerService>();

// Register the resolver
builder.Services.AddSingleton<LoggerResolver>();

// Swagger
builder.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


Why This is a Real-World Design

  • 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


Future Enhancements

  • 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


Conclusion

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.


GitHub Repository

I’ve shared this code in a GitHub repo:

Repo name and Link: StrategyPatternLogging


Writer: Ravinder Singh

Full Stack Developer

I have 15+ years of experience in commercial software development. I write this blog as a kind of knowledge base for myself. When I read about something interesting or learn anything I will write about it. I think when writing about a topic you concentrate more and therefore have better study results. The second reason why I write this blog is, that I love teaching and I hope that people find their way on here and can benefit from my content.

Ready to Transform Your Digital Presence?

Let's discuss how these trends can benefit your business

Free Consultation