Search code examples
loggingxamarin.formsprismdryioc

Register ILoggerFactory in DryIoc container


I want to use Microsoft.Extensions.Logging with a DryIoc container.

The default way is to register the factory as an instance, inject it and create a logger:

ILoggerFactory loggerFactory = new LoggerFactory().AddDebug();
container.RegisterInstance(loggerFactory);

// inject factory in constructor
public MyService(ILoggerFactory loggerFactory){
 this.logger = loggerFactory.CreateLogger<MyService>()

 this.logger.LogInformation("Logger created");
}

But I want a more ASP.NET Core like behavior, where the Logger is directly injected:

// inject logger in constructor
public MyService(ILogger<MyService> logger){
 this.logger = logger;

 this.logger.LogInformation("Logger created");
}

So I need to create an Instance of the ILoggerFactory due to some configuration, and the ILogger<> Interface must be registered in the container using the "CreateLogger" Method when requested.

I tried using registration with a factory method from https://bitbucket.org/dadhi/dryioc/wiki/SelectConstructorOrFactoryMethod without any success.

Ended up with something like this, but the generic in CreateLogger<> is missing:

container.Register(typeof(ILogger<>), made: Made.Of(() => loggerFactory.CreateLogger<>()));

Maybe anyone can help.

Edit

You have to do this for getting the correct factory method:

var loggerFactoryMethod = typeof(LoggerFactoryExtensions).GetMethod("CreateLogger", new Type[] { typeof(ILoggerFactory) });

I've created and updated a working sample of this using Xamarin.Forms (with Prism and DryIoc) at github.com/dernippel/PrismNetCoreLoggingApp


Solution

  • Here is the full working example based on sample interfaces and classes.
    Live code to play is here.

    using System;
    using DryIoc;
    
    public class Program
    {
        public static void Main()
        {   
            var container = new Container();
    
            // note usage of UseInstance instead of obsolete RegisterInstance
            container.UseInstance(new LoggerFactory()); 
    
            var loggerFactoryMethod = typeof(LoggerFactory).GetMethod("CreateLogger");
    
            container.Register(typeof(ILogger<>), made: Made.Of(
                req => loggerFactoryMethod.MakeGenericMethod(req.Parent.ImplementationType),
                ServiceInfo.Of<LoggerFactory>()));
    
            container.Register<MyService>();
    
            container.Resolve<MyService>();
        }
    
        class MyService 
        {   
            public MyService(ILogger<MyService> logger) { logger.Log("Hey!"); }
        }
    
        interface ILogger<T> 
        {
            void Log(string msg);
        }
    
        class ConsoleLogger<T> : ILogger<T>
        {
            public void Log(string msg) { Console.WriteLine(typeof(T) + ": " + msg); }
        }
    
        class LoggerFactory 
        {
            public ILogger<T> CreateLogger<T>() { return new ConsoleLogger<T>(); }
        }
    }
    

    Update for static LoggerFactoryExtensions

    For the static method the setup is more simple, you don't need to specify a factory instance via ServiceInfo.Of<LoggerFactory>(), it will be injected as any other parameter.

    Here is the changes (live sample is updated):

        var loggerFactoryMethod = typeof(LoggerFactoryExtensions).GetMethod("CreateLogger");
    
        container.Register(typeof(ILogger<>), made: Made.Of(
            req => loggerFactoryMethod.MakeGenericMethod(req.Parent.ImplementationType)));
    

    Given the extensions:

    public static class LoggerFactoryExtensions
    {
        public static ILogger<T> CreateLogger<T>(this LoggerFactory f) { return new ConsoleLogger<T>(); }
    }