Does injecting ILogger<T> create a new logger each time? - c#

On the logging samples in the documentation, there is an example how to inject a logger into a controller:
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger _logger;
public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
}
}
Does the DI framework create a new logger each time I inject a logger into something like here? Is there a better way?

This is easily answered by a look into the source. When you do services.AddLogging(), the default behavior is that ILogger<T> is registered as a singleton:
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
{
// …
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
// …
}
So no, ILogger<T> instances for a certain type T are kept around for as long as the application is running. So when injecting an ILogger<TodoController> into your controller, the same logger instance will be passed to it every time.
Of course this only applies to the logger, but not your controller itself. By default, controllers are activated outside of DI but effectively live with a scoped lifetime. So on every request, there will be a new controller instance; but that one will then get the logger instance from before.
To answer your last question, is there a better way? No. Apart from the fact that this behavior is already a good one (since there’s no need for new logger instances), the proper way to use logging is indeed to inject ILogger<T> into types T, so you get a properly categorized logger instance. There’s really no need to worry about the very thin logger here when there are so many way more expensive things going on in the background that you will likely never see ;)
Since the ILogger<T> is a singleton, its instance will be reused all throughout the application. Note that this will not have an effect on logging scopes. The ILogger<T> implementation that you use within your application is actually just a thin wrapper that forwards logging calls to the internal loggers (which are also effectively singletons). So the lifetime of ILogger<T> is actually not relevant since they do not keep any state at all.
The logging scopes themselves are persisted using an AsyncLocal which is a mechanism to keep state throughout the asynchronous call flow. That means that logging scopes will just “magically” work and not leak outside of the call flow just because some instances happen to be shared between multiple threads (or asynchronous flows).

Related

Dependency injection using both AddSingleton and AddTransient C#

I'm having a huge problem with the configuration/dependency injection of an application.
I have a singleton class added through DI with AddSingleton, that has in its constructor a IRequestClient, that is scoped because
busConfigurator.AddRequestClient()
which among other things, has the same effect as AddScoped.
When I start the app, it says
"Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'.)"
Which absolutely makes sense.
The weirdest thing is that I have another app set up the exact same way, but it just works and I would really like for that class to remain singleton.
My colleague and I spent an entire day trying to find the differences between the two applications, but they are virtually the same in their configurations, so we are having trouble in understanding why one works while the other doesn't.
I'm not entirely sure on what details could be important to better define the problem, so feel free to ask.
We've looked all around the internet trying to find a solution, but it was always "Change singleton to transient", but that's not an option, first because it HAS to be a singleton, otherwise it wouldn't make sense in our app, as that thing is what caches lots of date from our db so we can't just go around keeping on collecting heaps of data, second because the first app works with singleton, not with transient and we'd like to keep it that way
// This method is called in Main()
private static void ConfigureMassTransit(IServiceCollection services)
{
services.AddMassTransit(busConfigurators =>
{
busConfigurators.AddRequestClient<ICacheRepository>();
busConfigurators.AddConsumers(typeof(Program).GetTypeInfo().Assembly);
busConfigurators.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri($"rabbitmq://{Config.Settings.RabbitMq_Host}"), hostConfigurator =>
{
hostConfigurator.Username(Config.Settings.RabbitMq_User);
hostConfigurator.Password(Config.Settings.RabbitMq_Password);
});
cfg.ReceiveEndpoint("myApp", e =>
{
e.ConfigureConsumers(context);
});
});
});
// CacheRepository
public class CacheRepository : ICacheRepository
{
private readonly IClient Client;
public CacheRepository(ICacheRepository client, ILogger<CacheRepository> logger)
{
this.client = client;
this.logger = logger;
}
}
When a dependency is scoped, the implication is that a new instance is needed for each scope (which is usually an incoming HTTP request or message.) It implies that the instance should not be re-used for multiple requests.
If you have a singleton that depends on that scoped dependency, that singleton will be created using an instance of that dependency (the request client.) Because that singleton "lives forever," so does the instance of the request client it contains.
The result is that the request client is not supposed to be re-used across different scopes, but now it is. One instance is used forever.
A likely solution is to modify the class that depends on that client so that it doesn't need to be a singleton. You mentioned that it has to be a singleton because it caches data.
How does it cache data? Does it do so by storing data in a private field? If so, perhaps you could make that field static. Now the class instance isn't re-used, but those fields are shared between instances. (Verify that interaction with those fields is thread safe if they may be accessed concurrently.)
Or if there's some other cache mechanism, you could move that into its own dependency and make that a singleton.
Then your class can be scoped. It will depend on the singleton cache, always using the same instance. It will also depend on the scoped request client, using a new instance for each scope.
You could inject IServiceProvider instead, and create a scope when the singleton needs to perform a request. That way, you're sticking to the expected use of the request client.
await using var scope = provider.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IRequestClient<T>>();
await client.GetResponse(...);

Injecting IServiceProvider into Factory Class with Autofac

I have a factory class in a Net Core 3 console app which needs to be able to resolve against a DI container at runtime:
public class OptionFactory : IOptionFactory
{
private readonly IServiceProvider _svcProvider;
public OptionFactory( IServiceProvider svcProvider )
{
_svcProvider = svcProvider;
}
public IOption<T>? CreateOption<T>( params string[] keys )
{
// code eliminated for brevity
try
{
return retVal = _svcProvider.GetRequiredService<Option<T>>();
}
catch( Exception e )
{
return null;
}
}
}
I'm using Autofac to define the DI container and then "assign" it to IServiceProvider via new AutofacServiceProvider( builder.Build() ) in a provider class:
public class TestServiceProvider
{
public static IServiceProvider Instance { get; private set; }
static TestServiceProvider()
{
var builder = new ContainerBuilder();
builder.RegisterType<OptionFactory>()
.As<IOptionFactory>()
.SingleInstance();
// code omitted for brevity
Instance = new AutofacServiceProvider( builder.Build() );
}
}
I'm unclear about how to register IServiceProvider itself with the DI container so that it can be injected into the constructor. Is that even possible? It seems a little self-referential, which could be problematic.
All the examples I've seen online call for referencing back to the Autofac IContainer itself, (or to TestServiceProvider.Instance in my example). I can do that, but it would end tie my library to a concrete service provider class. Which I think I'd like to avoid if I can.
I realize injecting IServiceProvider is considered an anti-pattern by some/many, although others deem it acceptable in a factory class because the factory is "simply" extending the DI container. I'm open to other approaches which don't rely on a factory class, provided they allow me to create concrete instances of open generic types at runtime.
You have a couple of options (no pun intended 😃).
Easiest: Call builder.Populate() with an empty collection
The Autofac.Extensions.DependencyInjection package (which you're using, since you have AutofacServiceProvider) has an extension method ContainerBuilder.Populate() which handles registering stuff from an IServiceCollection and auto-registering the AutofacServiceProvider. You could call that method with an empty service collection and it'll work.
builder.Populate(Enumerable.Empty<ServiceDescriptor>());
This will get you exactly the thing you're looking for. However, there's an alternative to consider...
Alternative: Use ILifetimeScope
If it doesn't matter whether your OptionFactory is tied to Autofac, you can inject ILifetimeScope. Autofac has the current lifetime scope auto-registered, so this will work:
public OptionFactory(ILifetimeScope scope)
{
// scope is whatever lifetime scope the
// factory itself came from - if that's the
// root container, then the scope is the
// container
}
The benefit here is you'll get the richer resolve options Autofac offers without any extra work. The drawback would be you're tied to Autofac at this level, which may or may not matter.
Beware!
It may just be your example, but there's something important to know if you're resolving directly from the root container the way the example shows:
You could easily end up with a big memory leak.
Autofac holds on to all IDisposable instances it resolves so they can be safely disposed when the lifetime scope is disposed. If you are resolving from the container, that means any IDisposable will be held onto until the container itself is disposed, which, for most, is the lifetime of the application. That means - hypothetically - every resolution could be adding just a tiny little bit of memory that won't be disposed until the container is disposed. Memory leak.
For this reason we recommend always resolving from a nested lifetime scope rather than from the container. In a web app, that request-level lifetime scope is perfect because it disappears after a request. In an example like this, it's up to you and your app code to determine the best way to integrate lifetime scopes.
And, of course, if you're definitely, 100% guaranteed never resolving anything IDisposable, no worries.

ASP.NET Core - Multiple instances of Logger

I am right now working with Logging using ASP.NET Core LoggerFactory (Serilog extension). I want to put logs in controller and business service methods. I have done that via constructor injection of ILogger like this
In controller:
ILogger<HomeController> _logger
In Service:
ILogger<ServiceName> _logger
I believe this will be instantiated at each request, so for each HTTP request, it will create multiple instances of Logger. Say for each controller and each service class but it is bit different than previous methods of logging where we used to create only one logger instance and use that for logging stuff everywhere.
Any downside for this?
This is totally fine. Typically, it's cheap to instantiate a logger, so it's totally OK to do it like that performance-wise.
Still, consider either (1) using the global log instance which Serilog has or (2) using static field initialized in a field declaration. Again, not for the performance reasons, but rather to avoid polluting your constructor with not-so-relevant stuff.
UPD Update on implementing (1)
Basically, it's just a matter of deciding where would you put the logger init code into. In a ASP.NET Core it would be the first line of Main method (that Log is a static class from Serilog namespace):
Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole(LogEventLevel.Debug, LogTemplate)
.WriteTo.File(#"C:\logs\elbakogdabot.log", LogEventLevel.Debug, LogTemplate)
.Enrich.FromLogContext()
.CreateLogger();
(just to be clear: I took the code from a real project of mine, but the actual configuration of your logger could be different).
Then I would use it anywhere like this:
Log.Warning($"got a message for an unknown user: userid=[{userId}]");
This line could be thrown into any class and you don't have to do any extra initialization for that class.
UPD Update on implementing (2)
I guess in a typical enterprise app it would be inconvenient to have always remember to put the class name in the message every time you logging something. So I would go with static readonly field most of the time. With Serilog you can do it like that:
public class XYZService
{
private static readonly Serilog.ILogger log = Log.ForContext<XYZService>();
...
This way you both won't pollute the constructor, and will get the class name in all of your log messages automatically. I used to have this line in a ReSharper snippet, so I had to just type lg<TAB> in every new class.

RegisterWithContext and Lifestyle Mismatch

I wanted to inject a logger to my controllers and I needed to pass extended info to the logger's constructor. For the purpose I've use RegisterWithContext:
container.RegisterWithContext<Common.Logging.ILogger>(context =>
{
if (context.ServiceType == null && !container.IsVerifying())
{
throw new InvalidOperationException(
"Can't request ILogger directly from container, " +
"it must be injected as a dependency.");
}
return new Common.Logging.NLogLogger(context.ImplementationType.FullName);
});
RegisterWithContext extension method explicitly registers the supplied delegate as Transient.
I need to inject the same Logger (Common.Logging.NLogLogger) in a service which happens to be singleton.
Before upgrading to SimpleInjector 3.0.6 things seemed to work as expected and container.Verify() was quite happy with the whole configuration.
After the upgrade the verifier returns a few errors:
[Lifestyle Mismatch] SearchEngineIndexerService (Singleton) depends on
ILogger (Transient). [Lifestyle Mismatch] MembershipService (Web
Request) depends on ILogger (Transient).
and it makes sense. I can understand why that happens and why it should be avoided.
I am trying to avoid the "Do I log too much" syndrome but, actually, I really need to do some logging in a couple of services.
I've tried to use RegisterConditional to register a different logger based on certain conditions but, of course, all the logger now should be registered conditional or I get this exception:
Type ILogger has already been registered as unconditional registration. For non-generic types, conditional and unconditional registrations can't be mixed.
What's the best approach to register a logger as transient for a controller and another one for a singleton service?
The reason you are seeing this exception now, is because v3.0.6 fixed some bugs that prevented the lifestyle mismatch warning from showing up in certain occasions.
It is best to ignore the RegisterWithContext extension method, because it has been superseded by the RegisterConditional method in v3. RegisterConditional however, only allows registering types; not delegates, because delegates allow you to make decisions based on runtime decisions, but it is bad practice to make runtime decisions during object graph resolution.
So instead, it is best to define a proxy logger class that allows forwarding the call to the real logger. For instance:
public sealed class Logger<T> : ILogger
{
private static readonly ILogger logger =
new Common.Logging.NLogLogger(typeof(T).FullName);
// Implement ILogger methods here
void ILogger.Log(string message) {
// Delegate to real logger
logger.Log(message);
}
}
This implementation can be registered as follows:
container.RegisterConditional(typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
The documentation describes this in more detail.

Simple Injector: Inject same UnitOfWork instance across services of the same graph

I have multiple services, each of which have a UnitOfWork injected into the constructor using the Simple Injector IoC container.
Currently I can see each UnitOfWork instance is a separate object, this is bad as i am using Entity Framework and require the same context reference across all units of work.
How can I ensure the same UnitOfWork instance is injected into all services per each resolve request? My UnitOfWor will be saved by an external command handler decorator after the command completes.
Please note, this is a common library and will be used for both MVC and Windows Forms, it would be nice to have a generic solution for both platforms if possible.
Code is below:
// snippet of code that registers types
void RegisterTypes()
{
// register general unit of work class for use by majority of service layers
container.Register<IUnitOfWork, UnitOfWork>();
// provide a factory for singleton classes to create their own units of work
// at will
container.RegisterSingle<IUnitOfWorkFactory, UnitOfWorkFactory>();
// register logger
container.RegisterSingle<ILogger, NLogForUnitOfWork>();
// register all generic command handlers
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
// register services that will be used by command handlers
container.Register<ISynchronisationService, SynchronisationService>();
container.Register<IPluginManagerService, PluginManagerService>();
}
The desired outcome of the below line is to create a object which has a shared UnitOfWork instance throughout the constructed object graph:
var handler = Resolve<ICommandHandler<SyncExternalDataCommand>>();
Here are my services:
public class PluginManagerService : IPluginSettingsService
{
public PluginManagerService(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
private readonly unitOfWork;
void IPluginSettingsService.RegisterPlugins()
{
// manipulate the unit of work
}
}
public class SynchronisationService : ISynchronisationService
{
public PluginManagerService(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
private readonly unitOfWork;
void ISynchronisationService.SyncData()
{
// manipulate the unit of work
}
}
public class SyncExternalDataCommandHandler
: ICommandHandler<SyncExternalDataCommand>
{
ILogger logger;
ISynchronisationService synchronisationService;
IPluginManagerService pluginManagerService;
public SyncExternalDataCommandHandler(
ISynchronisationService synchronisationService,
IPluginManagerService pluginManagerService,
ILogger logger)
{
this.synchronisationService = synchronisationService;
this.pluginManagerService = pluginManagerService;
this.logger = logger;
}
public void Handle(SyncExternalDataCommand command)
{
// here i will call both services functions, however as of now each
// has a different UnitOfWork reference internally, we need them to
// be common.
this.synchronisationService.SyncData();
this.pluginManagerService.RegisterPlugins();
}
}
Which registration you need depends on the type of application. Since you are talking about two different frameworks (MVC and WinForms), both will have a different registration.
For an MVC application (or web applications in general), the most common thing to do is to register the unit of work on a per web request basis. For instance, the following registration will cache the unit of work during a single web request:
container.Register<IUnitOfWork>(() =>
{
var items = HttpContext.Current.Items;
var uow = (IUnitOfWork)items["UnitOfWork"];
if (uow == null)
{
items["UnitOfWork"] = uow = container.GetInstance<UnitOfWork>();
}
return uow;
});
The downside of this registration is that the unit of work is not disposed (if needed). There is an extension package for the Simple Injector that adds RegisterPerWebRequest extension methods to the container, which will automatically ensure that the instance is disposed at the end of the web request. Using this package, you will be able to do the following registration:
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
Which is a shortcut to:
container.Register<IUnitOfWork, UnitOfWork>(new WebRequestLifestyle());
A Windows Forms application on the other hand, is typically single threaded (a single user will be using that application). I believe it is not unusual to have a single unit of work per form, which is disposed the form closes, but with the use of the command/handler pattern, I think it is better to take a more service oriented approach. What I mean by this is that it would be good to design it in such way that you can move the business layer to a WCF service, without the need to make changes to the presentation layer. You can achieve this by letting your commands only contain primitives and (other) DTOs. So don't store Entity Framework entities into your commands, because this will make serializing the command much harder, and it will lead to surprises later on.
When you do this, it would be convenient to create a new unit of work before the command handler starts executing, reuse that same unit of work during the execution of that handler, and commit it when the handler completed successfully (and always dispose it). This is a typical scenario for the Per Lifetime Scope lifestyle. There is an extension package that adds RegisterLifetimeScope extension methods to the container. Using this package, you will be able to do the following registration:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();
Which is a shortcut to:
container.Register<IUnitOfWork, UnitOfWork>(new LifetimeScopeLifestyle());
The registration however, is just half of the story. The second part is to decide when to save the changes of the unit of work, and in the case of the use of the Lifetime Scope lifestyle, where to start and end such a scope. Since you should explicitly start a lifetime scope before the command executes, and end it when the command finished executing, the best way to do this, is by using a command handler decorator, that can wrap your command handlers. Therefore, for the Forms Application, you would typically register an extra command handler decorator that manages the lifetime scope. This approach does not work in this case. Take a look at the following decorator, but please note that it is incorrect:
private class LifetimeScopeCommandHandlerDecorator<T>
: ICommandHandler<T>
{
private readonly Container container;
private readonly ICommandHandler<T> decoratedHandler;
public LifetimeScopeCommandHandlerDecorator(...) { ... }
public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
// WRONG!!!
this.decoratedHandler.Handle(command);
}
}
}
This approach does not work, because the decorated command handler is created before the lifetime scope is started.
We might be tempted into trying to solve this problem as follows, but that isn't correct either:
using (this.container.BeginLifetimeScope())
{
// EVEN MORE WRONG!!!
var handler = this.container.GetInstance<ICommandHandler<T>>();
handler.Handle(command);
}
Although requesting an ICommandHandler<T> inside the context of a lifetime scope, does indeed inject an IUnitOfWork for that scope, the container will return a handler that is (again) decorated with a LifetimeScopeCommandHandlerDecorator<T>. Calling handler.Handle(command) will therefore result in a recursive call and we'll end up with a stack overflow exception.
The problem is that the dependency graph is already built before we can start the lifetime scope. We therefore have to break the dependency graph by deferring building the rest of the graph. The best way to do this that allows you to keep your application design clean] is by changing the decorator into a proxy and injecting a factory into it that will create the type that it was supposed to wrap. Such LifetimeScopeCommandHandlerProxy<T> will look like this:
// This class will be part of the Composition Root of
// the Windows Forms application
private class LifetimeScopeCommandHandlerProxy<T> : ICommandHandler<T>
{
// Since this type is part of the composition root,
// we are allowed to inject the container into it.
private Container container;
private Func<ICommandHandler<T>> factory;
public LifetimeScopeCommandHandlerProxy(Container container,
Func<ICommandHandler<T>> factory)
{
this.factory = factory;
this.container = container;
}
public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
var handler = this.factory();
handler.Handle(command);
}
}
}
By injecting a delegate, we can delay the time the instance is created and by doing this we delay the construction of (the rest of) the dependency graph. The trick now is to register this proxy class in such way that it will inject the wrapped instances, instead of (of course) injecting itself again. Simple Injector supports injecting Func<T> factories into decorators, so you can simply use the RegisterDecorator and in this case even the RegisterSingleDecorator extension method.
Note that the order in which decorators (and this proxy) are registered (obviously) matters. Since this proxy starts a new lifetime scope, it should wrap the decorator that commits the unit of work. In other words, a more complete registration would look like this:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
// Register a decorator that handles saving the unit of
// work after a handler has executed successfully.
// This decorator will wrap all command handlers.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
// Register the proxy that starts a lifetime scope.
// This proxy will wrap the transaction decorators.
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(LifetimeScopeCommandHandlerProxy<>));
Registering the proxy and decorator the other way around would mean that the TransactionCommandHandlerDecorator<T> would depend on a different IUnitOfWork than the rest of the dependency graph does, which would mean that all changes made to the unit of work in that graph will not get committed. In other words, your application will stop working. So always review this registration carefully.
Good luck.

Categories