Implementing Dependency Injection With A Static Logger In C#


We are working on a console application project, essentially a service for a payment gateway app. In this project, I'm responsible for handling the backend service for updating payment statuses. I have a static class that calls a static Logger class for logging application errors and information. Now, I'm unsure of how to inject the Logger into my static class. 
If you're also facing the same issue, you've come to the right place. Let's take a look at the solution.

A static logger that we can't directly inject, we have a couple of options to work around this ,one approach is to refactor the logger into an instance logger if possible, or create a wrapper around the static logger to achieve the behavior. Since injecting dependencies into a static class can be challenging due to the lack of control over the static constructor, passing dependencies as method parameters becomes a pragmatic solution.

Using an Instance Logger:

If we're able to change the logger to an instance logger, we can easily inject it into our classes. Here's how it might look:


using System;

public interface ILogger
{
    void Log(string message);
}

public class ApplicationLogger
{
    private readonly ILogger _logger;

    public ApplicationLogger(ILogger logger)
    {
        _logger = logger;
    }

    public void DoSomething()
    {
        _logger.Log("Doing something...");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ILogger logger = new InstanceLogger();
        ApplicationLogger realClass = new ApplicationLogger(logger);
        realClass.DoSomething();
    }
}
    

Using a Wrapper for Static Logger:

If changing the logger to an instance logger is not feasible, we can create a wrapper around the static logger and inject it as a dependency:


using System;

public interface ILogger
{
    void Log(string message);
}

public static class StaticLoggerWrapper
{
    public static ILogger Logger { get; set; }

    public static void Log(string message)
    {
        if (Logger != null)
            Logger.Log(message);
        else
            Console.WriteLine("[Static Logger] " + message);
    }
}

public class ApplicationLogger
{
    private readonly ILogger _logger;

    public ApplicationLogger(ILogger logger)
    {
        _logger = logger;
    }

    public void DoSomething()
    {
        _logger.Log("Doing something...");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ILogger logger = new InstanceLogger();
        StaticLoggerWrapper.Logger = logger;

        ApplicationLogger realClass = new ApplicationLogger(logger);
        realClass.DoSomething();
    }
}
    

In both examples, we've discuessed how to work with a static logger by either converting it to an instance logger or wrapping it in a wrapper class, to  allows us to inject the logger into our classes, promoting better testability and flexibility in our code.

Implementing dependency injection with static classes like a logger and a helper class in C#, we need to carefully consider the limitations and possible solutions. Static classes do not support constructor injection like regular classes, but we can still achieve dependency injection by employing the singleton pattern along with dependency injection containers.

Let's start by defining a static logger class:


    public interface ILogger
    {
        void Log(string message);
    }

    public static class Logger
    {
        private static ILogger _logger;

        public static void Initialize(ILogger logger)
        {
            _logger = logger;
        }

        public static void LogMessage(string message)
        {
            if (_logger != null)
            {
                _logger.Log(message);
            }
            else
            {
                // Handle the case where the logger hasn't been initialized
                Console.WriteLine("Logger not initialized. Logging to console: " + message);
            }
        }
    }
    

Next, let's create a static helper class that depends on the logger:


    public static class Helper
    {
        public static void DoSomething()
        {
            // Example logic
            Logger.LogMessage("Doing something important...");
            // More logic
        }
    }
    

Now, we'll configure dependency injection using a DI container in our application startup:


    // ConfigureServices method in Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton();
    }
    

With this configuration, the MyLoggerImplementation will be registered as a singleton instance of ILogger.

Finally, we need to initialize the logger and use it in our application:


    // Startup.cs or Program.cs
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Other configurations

        // Initialize Logger class with ILogger instance
        Logger.Initialize(app.ApplicationServices.GetRequiredService());

        // Now, we can use the helper class
        Helper.DoSomething();
    }