How container actually resolve ILogger<T> - c#

In ASP.NET Core 2, when we talking about logging, it is enough to just register providers via ILoggerFactory.AddProvider() or to use appropriate extension methods. Then you would be able to resolve ILogger<T> (without registration of closed ILogger<>'s). I'm struggling with a bridge between ILoggerFactory.CreateLogger<T> invocation and actual ILogger<T> resolution: seems like somewhere magic happens, but I didn't find where.
Any ideas?

It all starts in ConfigureLogging:
public static IWebHostBuilder ConfigureLogging(
this IWebHostBuilder hostBuilder,
Action<ILoggingBuilder> configureLogging)
{
return hostBuilder.ConfigureServices(
collection => collection.AddLogging(configureLogging));
}
The call of importance here is to AddLogging. I won't include the entire function here, but the registration process looks like this:
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
As can be seen above, the ASP.NET Core Dependency Injection container allows for the registration of open generics.
Finally, the constructor for Logger<T> looks like this:
public Logger(ILoggerFactory factory)
{
// ...
_logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));
}
The rest of the Logger<T> class is just a passthrough to the non-generic _logger, which now includes the name of T's type.

Related

How can Autofac and an Options pattern be used in a .NET 5 console application?

I am trying to use an option pattern with Autofac and every attempt has just resulted in errors.
What I've tried:
Using the ConfigurationBuilder to retrieve an IConfiguration/IConfigurationRoot.
Register an instance of TestSectionOptions using the IConfiguration/IConfigurationRoot that was created before:
builder.Register(c => config.GetSection("TestSection").Get<TestSectionOptions>());
Trying to inject it via constructor injection:
private readonly TestSectionOptions _options;
public DemoClass(IOptions<TestSectionOptions> options)
{
_options = options.Value;
}
I'm getting following error:
DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'DemoApp.DemoClass' can be invoked with the available services and parameters:
Cannot resolve parameter
'Microsoft.Extensions.Options.IOptions1[DemoApp.TestSectionOptions] options' of constructor 'Void .ctor(Microsoft.Extensions.Options.IOptions1
Of course I tried other types of registration, but none of them worked.
I also know that I can simply bind the configuration file to a class, which I then register and inject without the IOptions<> part. But that would no longer correspond exactly to the option pattern, would it?
Even if it doesn't make a big difference, I'd still like to know why it doesn't work and how I could get it to work.
The problem is that this IOptions type should be registerd somewhere.
You can see e.g. this article. There is an example
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
So, somewhere inside Configure extension method it registers types for options, among others IOptions<>.
So, in your case you either have to do this explicitly, like
builder.Register(c => Options.Create(config.GetSection("TestSection").Get<TestSectionOptions>()))
This will register IOptions
or, you can create an empty service collection, then call Configure method on it, and then copy all registrations to autofac builder - there is Populate method from the package "Autofac.Extensions.DependencyInjection"
https://autofac.org/apidoc/html/B3162450.htm

Unit Testing with ILoggerProvider

I have the code like this in my unit test setup:
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IDependency, Dependency>();
services.AddLogging();
services.RemoveAll<ILoggerProvider>();
services.AddSingleton<ILoggerProvider, MyCustomProvider>(provider =>
{
var myDependency = provider.GetService<IDependency>();
return new MyCustomProvider(myDependency );
});
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger>();
When I run this logger is null.
The lambda for the DI factory to create MyCustomProvider is never called. Nor is the constructor to MyCustomProvider nor the constructor of the class I made that implements ILogger.
I am guessing that I am missing a step to wire this up. But there is a lot of conflicting documentation out there between .Net Core 1, 2 and 3.
What do I need to do to wire up my provider the .Net Core 3 way? (Such that I can get an ILogger that uses it.)
NOTES:
I don't call LoggingBuilder.AddProvider. But if you look at the source code for that extension, it just calls AddSingleton, which is what I do, but with a factory. (Got the idea here.)
Similarly services.RemoveAll<ILoggerProvider>() is the same as calling ClearProviders.
My goal is to simulate an injection of an ILogger into a class constructor.
For Example:
public class SomeClass
{
public SomeClass(ILogger<SomeClass> logger)
{
// Do Stuff
}
}
Somehow this is done without indicating any provider. I would like to make it be the same in my unit test. (Just use my custom provider.)
The reason your logger variable is null in the example is because ILogger isn't actually registered as a service. If you look at the source of AddLogging you can see it only registers ILogger<> and ILoggerFactory. If you've ever tried to accept an ILogger instead of an ILogger<MyClass> via the .NET Core DI you will have run into the following exception*:
System.InvalidOperationException: 'Unable to resolve service for type
'Microsoft.Extensions.Logging.ILogger' while attempting to activate
'Your.Service'
Based on this, your testing code is flawed as you'd never have received an ILogger in the first place. To see that you're code is in fact working, modify your testing code so that it retrieves an ILogger<SomeClass> instead. The result of your variable will be non-null and a break point set in your provider's constructor will be hit:
// Get*Required*Service won't throw
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
If you want to be able to inject ILogger, you will need to register it separately with a default category name**:
services.AddSingleton<ILoggerProvider, MyCustomProvider>(); // no need for lambda
services.AddSingleton<ILogger>(sp =>
sp.GetService<ILoggerFactory>().CreateLogger("Default")
);
The following will both now work and use your custom provider:
var loggerA = serviceProvider.GetRequiredService<ILogger<Program>>();
var loggerB = serviceProvider.GetRequiredService<ILogger>();
I have my own custom provider/factory/logger that I use to inject xUnit's ITestOutputHelper into a custom logger following the same registration pattern as you, so I know from personal experience that this works. But I've also tested that your specific code is functional (mocking out my own IDependency)--the above code is executed and breakpoints set in the constructor of MyCustomProvider and the service registration are hit. Additionally, if I inject the logger into a class it's hit as well (as expected).
Your comment "Somehow this is done without indicating any provider" is misinformed, because you do in fact have a provider registered! But even in a scenario where all providers were cleared and no new ones were added, you'd still get a non-null logger. This is because LoggerFactory just loops through each provider to build up a new logger wrapper around them. If there's no providers then you essentially get a no-op logger.
* This is unfortunate as some tools (like R#) will suggest converting the parameter to the base type ILogger which then breaks DI!
** A default category name is required since there is no generic type argument to pass to ILoggerFactory.CreateLogger. For the generic ILogger<T> the category name is always a variation of T's name--but we obviously don't get that with the non-generic version. If I had to guess, this is probably why they don't register an implementation of ILogger by default.
NET CORE Testing I'm doing like this:
in the constructor of the test class (or where you do your DI mapping)
public class UnitTest1
{
public IConfigurationRoot Configuration { get; set; }
private readonly IDisponibilitaService _disponibilitaService;
public UnitTest1()
{
var services = new ServiceCollection();
Configuration = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Path.DirectorySeparatorChar.ToString(), "directory", "Kalliope_CTI", "backend", "KalliopeCTITestProject"))
.AddJsonFile("testconfig.json", optional: false, reloadOnChange: true)
.Build();
services.AddSingleton<ILoggerFactory, NullLoggerFactory>(); //< -- HERE TRY TO SET NullLoggerFactory
var serviceProvider = services
.AddOptions()
.BuildServiceProvider();
}
//// your test methods
}
Hope it helps you!!
It may be a shot in the dark, but you can try adding your ILoggerProvider to the ILoggerFactory:
var loggerFactory = serviceProvider.GetService<ILoggerFactory();
var loggerProvider = serviceProvider.GetService<ILoggerProvider>();
loggerFactory.AddProvider(loggerProvider);
In case it was constructed sooner than you have registered your provider, it might not be aware of it's existance.
Alternatively, would it work, if you also tell it, how to resolve the ILogger?:
...
services.AddSingleton<ILoggerProvider, MyCustomProvider>(provider =>
{
var myDependency = provider.GetService<IDependency>();
return new MyCustomProvider(myDependency );
});
services.AddSingleton(typeof(ILogger<>), provider =>
{
var loggerProvider = provider.GetService<ILoggerProvider>();
return loggerProvider.CreateLogger("TestLogger");
});
...
Finally... is there a reason, why you can't just inject ILoggerProvider into your tests, and call loggerProvider.CreateLogger(<test_class_name>) to get the logger?

How to use NLog in static class where everything else is wired with Autofac

My MVC app is wired with Autofac. I have also configured NLog which works as expected in my controller classes. My nLogger is registered as below:
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(LoggerService<>))
.As(typeof(ILoggerService<>)).SingleInstance();
var container = builder.Build();
And the constructor of the ILoggerService is:
public LoggerService()
{
SourceClass = typeof (T).FullName;
Logger = LogManager.GetLogger(SourceClass);
}
Now I have also got many static helper classes that I use. For example:
public static class Helper
{
public static string GenerateQrBitmap(string secret, string issuer, string userEmail)
{
...
}
}
But I want to be able to use the logger in these Helper classes as well.
This is one of the reasons why static classes aren't great.
You have two options:
Make them not static (and register them with Autofac) and take
ILoggerService as a constructor parameter.
Change their methods
(e.g. GenerateQrBitmap) to take a ILoggerService as a parameter.
I'd suggest the former.
The alternative is to use the Service Locator pattern - and have Helper resolve directly against the container. I will not show you how to do this, since I don't recommend it. It makes the code harder to unit test, and it hides your dependencies. But if you Google Autofac Service Locator static class c# I'm sure you'll work it out.

How to pass dependencies to a custom .NET Core ILoggerProvider

I am creating a custom .NET Core ILoggerProvider that requires some dependencies to be passed into its constructor.
I believe I am using a fairly common pattern to initialize my logging implementation; it looks something like this:
var services = new ServiceCollection();
// Register some services here
services.AddLogging(builder =>
{
builder.AddProvider(new DebugLoggerProvider());
});
var provider = services.BuildServiceProvider();
I want to add my new provider within the AddLogging block, in the same way that the DebugLoggerProvider is currently added.
My custom provider requires some other services to be passed into its constructor and since these are already registered with the ServiceCollection, I assume that I should be able to reference them. However, unlike methods such as AddSingleton, which have an overload that exposes the IServiceProvider, AddLogging doesn't seem to offer an equivalent.
Is there a simple way to achieve this, or am I attempting to do something that contradicts the way .NET Core logging was designed to be deployed?
UPDATE:
After experimenting with the suggestions proposed by #Nkosi, I can confirm that it is possible to get this to work by bypassing AddLogging and directly implementing what it does internally, as follows:
var services = new ServiceCollection();
// Register some services
services.AddSingleton<IMyService, MyService>();
// Initialize logging
services.AddOptions();
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddSingleton<ILoggerProvider>(p => new DebugLoggerProvider());
services.AddSingleton<ILoggerProvider>(p => new MyLoggerProvider("Constant value", p.GetService<IMyService>()));
var provider = services.BuildServiceProvider();
Now I am not sure if an extension already exists to do this but I see potential here.
First this is how AddProvider is defined in the source code repo.
public static ILoggingBuilder AddProvider(this ILoggingBuilder builder, ILoggerProvider provider) {
builder.Services.AddSingleton(provider);
return builder;
}
You could build up on that by making your own generic version
public static class MyLoggingBuilderExtensions {
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder)
where T: class, ILoggerProvider{
builder.Services.AddSingleton<ILoggerProvider, T>();
return builder;
}
}
which should allow the DI container to build up the object graph when resolved
services.AddLogging(builder =>
{
builder.AddProvider<CustomLoggerProvider>();
});
And there is room to extend this functionality, like adding your own overload that exposes the IServiceProvider and passing that on to the AddSingleton within the extension.
public static ILoggingBuilder AddProvider<T>(this ILoggingBuilder builder, Func<IServiceProvider, T> factory)
where T: class, ILoggerProvider {
builder.Services.AddSingleton<ILoggerProvider, T>(factory);
return builder;
}
And used
services.AddLogging(builder => {
builder.AddProvider<CustomLoggerProvider>(p => new CustomLoggerProvider("Constant value", p.GetService<IMyService>()));
});
Apologies for being a bit late to the party on this one, but I ran into exactly the same problem after having searched high and low. Inspired by the excellent entries in this page, I ended up with the solution below.
services.AddTransient<IMyLogRepository, LogRepository>();
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole()
.AddDbLoggerProvider(services.BuildServiceProvider().GetService<IMyLogRepository>());
});
services.AddSingleton(loggerFactory.CreateLogger("MyLogging"));
The key to this being:
services.BuildServiceProvider().GetService<IMyLogRepository>())
Which allowed me to link my database repository to the dbLogger object I created in a single extra line. In essence, it gives me the ability to pluck my DI database object an send it to the Logging service via standard ILoggerProvider and ILogger interfaces
I got a simple solution to work which is kinda lighter.
serviceCollection.AddLogging(logBuilder =>
{
logBuilder.AddConfiguration(theConfigRoot.GetSection("Logging"));
});
serviceCollection.AddSingleton<ILoggerProvider, MyLogProvider>();
However.... Instanciating the Provider keeps you from running in circular dependency problems--> The service you may want to inject soon want´s a logger himself^^

Simple Injector and default AccountContoller dependency issue

I have problem with Simple Injector in my Web Api project. I user default AccountController generated by VS.
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
In my configuration file I register:
var container = new Container();
// This is an extension method from the integration package.
container.RegisterWebApiFilterProvider(config);
container.RegisterWebApiControllers(config);
container.Register<IInitializeService, InitializeService>();
container.Register<IFolderRepository, FolderRepository>();
container.Register<IUserRepository, UserRepository>();
container.Register<ILogger, Logger>();
//Authentication Wrap
container.Register<IUserStore<User, Guid>, ApplicationUserStore>();
container.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>();
container.Register<ISecureDataFormat<AuthenticationTicket>,
SecureDataFormat<AuthenticationTicket>>();
container.Register<IDataProtector>(
() => new DpapiDataProtectionProvider().Create("ASP.NET Identity"));
container.Verify();
// 4. Register the container as MVC3 IDependencyResolver.
DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
I though that Simple Injector will be smart enough to handle all build in dependences responsible for default identity and i wont need to register them manually, but I'm getting exceptions if I wont register them.
Still getting an exception:
The constructor of type SecureDataFormat contains the parameter of type ITextEncoder with name 'encoder' that is not registered. Please ensure ITextEncoder is registered in the container, or change the constructor of SecureDataFormat.
Is there any way to handle that automatically?
I implemented a Web Api and I wrote this code.
This works for me
container.RegisterWebApiRequest<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>();
container.RegisterWebApiRequest<ITextEncoder, Base64UrlTextEncoder>();
container.RegisterWebApiRequest<IDataSerializer<AuthenticationTicket>, TicketSerializer>();
container.RegisterWebApiRequest<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"));
How is the container supposed to know which implementation of ITextEncoder you want to use in the constructor of SecureDataFormat?
You have to tell it which one to use. I think the rules basically go something like this:
if an interface is required (by a constructor) then it needs to know which implementation to use. If a concrete class is required it will automatically build an instance of that class (assuming it can resolve all the types that class needs).
As your SecureDataForms needs an interface you have to register one, otherwise its only option would be to 'guess' at which implementation you want and this could then go wrong silently if more than one implementation existed.
I just came up against this issue. I'm using ninject but you'll get the idea. Here is my binding:
kernel.Bind<ITextEncoder>().To<Base64UrlTextEncoder>();
According to the source, the only thing I ever see being used to new up an instance of SecureDataFormat is Base64UrlTextEncoder. So it seemed like a safe bet to use, but it's certainly not clear to me at first glance how to appropriately use this constructor overload.

Categories

Resources