4

I've got a PRISM (8.1.97) WPF application running on .net5.0 with the unity container.

What I would like to do is to assign Serilog as my logging provider, but inject the usual Microsoft.Extensions.Logging logger through the constructor of my DI registered services (eg, just use ILogger<MyService> logger in constructors).

After some digging around I found some clues here and here.

Of these, the second one is the most straightforward. Basically you add Prism.Container.Extensions and then in App.xaml.cs, override CreateContainerExtension(). This gives you a way to access a service collection that seems just like what you get with the built-in .net 5 DI.

Below my App class from App.xaml.cs...

public partial class App : PrismApplication
{
    private IConfigurationRoot configRoot;

    protected override Window CreateShell()
    {
        // Configures logger using json config file
        Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configRoot)
            .CreateLogger();

        return Container.Resolve<MainWindow>();
    }


    protected override IContainerExtension CreateContainerExtension()
    {
        configRoot = new ConfigurationBuilder()
                    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", optional: false)
                    .Build();

        IContainerExtension containerExtension = base.CreateContainerExtension();

        containerExtension.RegisterServices(
            services => 
            {
                // Makes serilog the logging provider?
                services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true)); 
            });

        return containerExtension;
    }    
    

    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
    {
        // just a simple module with Views/viewModels
        var moduleAType = typeof(DevTestingModule);
        moduleCatalog.AddModule(new ModuleInfo()
        {
            ModuleName = moduleAType.Name,
            ModuleType = moduleAType.AssemblyQualifiedName,
            InitializationMode = InitializationMode.WhenAvailable
        });
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        // intentionally empty
    }
}

The above seems reasonable, but the problem is when I try to inject a logger, for example in MainWindow (the Prism Shell), it gives an exception upon Container.Resolve<MainWindow>(); in CreateShell() in App.xaml.cs.

Here's my MainWindow.xaml.cs class, when I try to inject a logger...

public partial class MainWindow : Window
{
    private readonly ILogger<MainWindow> log;

    public MainWindow(ILogger<MainWindow> log)
    {
        InitializeComponent();
        this.log = log;
        log.LogInformation("hello?");
    }
}

The Exception is a Prism.Ioc.ContainerResolutionException and the most notable inner exception is ...

No public constructor is available for type System.IServiceProvider.

I think I bungled something, but I am not sure if I am even on the right track here. It seems like the intention of the CreateContainerExtension() override from Prism.Container.Extensions is to give me a IServiceCollection that can be used like in Microsoft's DI from .net 5. Am I misinterpreting it?

Angelo
  • 2,936
  • 5
  • 29
  • 44
  • 1
    Can you clearly write what exactly you want to do without hiding all info in links? Do you want to inject a `Logger` into `T`'s constructor as `ILogger`? – Haukinger Sep 21 '21 at 19:07
  • @Haukinger, thanks! Updated my question with more details about what I've been trying. – Angelo Sep 22 '21 at 20:49
  • I have exactly the same demand and problem. Did you ever find a solution? Why is RegisterTypes left empty? – EuroEager Nov 09 '21 at 05:27
  • 1
    @EuroEager Yes, I should have update (will do so later). I got it to work by carefully following instructions on the first link in my question. This one: https://www.andicode.com/prism/wpf/logging/2021/05/21/Logging-In-Prism.html . I could not get the advice from the second link to work. – Angelo Nov 09 '21 at 18:03
  • Thanks, I tried that solution, but I think I missed adding the Serilog.Extensions.Logging package, working fine now – EuroEager Nov 09 '21 at 21:18
  • If you are using Prism, then you might have a MainWindowViewModel. You can inject your logger in its constructor. Of course, for that, you should move the code-behind of your MainWindow to the MainWindowViewModel... As a reminder, you can have several viewmodel constructors in Prism, the IOC will try to use the one with the most arguments. – Francois Denis Jun 14 '22 at 15:54
  • @FrancoisDenis, the problem was not constructor injection, but rather registering serilog as a logging implementation. Serilog provides an extension method for doing that within Microsoft DI, but it's not obvious how to do that in with Unity DI (which is what PRISM is using in this example). – Angelo Aug 03 '22 at 17:42
  • Could you share the full final solution? I'd used the anicode link previously, however it looks like the site has been hijacked, redirects to a bunch of spyware sites now. – user3265613 Jul 20 '23 at 13:46

0 Answers0