I am using ASP.NET Core, I know that such Logging mechanism is already provided by the framework, but using this to illustrate my problem.
I am using kind of Factory pattern to build the Logger class, since I don't know the type of logging (because it is stored in DB).
The ILogger Contract
Log(string msg)
Then LoggerFactory will return an ILogger after creating a Logger based on param passed from DB:
public class LoggerFactory
{
public static Contracts.ILogger BuildLogger(LogType type)
{
return GetLogger(type);
}
//other code is omitted, GetLogger will return an implementation of the related logger
Now, when I need to use the Logger I have to do it in this way:
public class MyService
{
private ILogger _logger
public MyService()
{
_logger = LoggerFactory.BuildLogger("myType");
}
But, I intend to keep my classes without any instantiation, I need to use Constructor DI in MyService and I need to inject all the dependencies on Startup:
services.AddTransient<Contracts.ILogger, LoggerFactory.BuildLogger("param") > ();
But this will not work this we need to pass a concrete implementation. How to make that work using DI, is there a better approach for implementing that?
Factory pattern can be called as a tool to implement DI. Dependency injection can be implemented in many ways like DI using constructors, using mapping xml files etc. That's true that Factory Pattern is one of the way implementing Dependency Injection.
Use Transient lifetime for the lightweight service with little or no state. Scoped services service is the better option when you want to maintain state within a request. Singletons are created only once and not destroyed until the end of the Application. Any memory leaks in these services will build up over time.
The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container. The ServiceProvider ensures that resolved services live for the expected lifetime.
There are a couple of errors in your approach:
LoggerFactory
type which is a Dependency Inversion Principle violation.ILogger
is the real service that your consumer depends upon. This makes the system harder to test, harder to maintain, and complicates object graph analysis.Instead, your service should look as follows:
public class MyService
{
private ILogger _logger;
public MyService(ILogger logger)
{
_logger = logger;
}
}
This dramatically simplifies all consumers that depend upon ILogger
. This also means that getting the right ILogger
for MyService
becomes a responsibility of the Composition Root, which is the correct place to have this knowledge.
It does mean however that you might need to move away from the built-in DI container of ASP.NET Core to a more feature rich DI library, because the built-in container is not capable of making a context aware registration for ILogger
while having the library auto-wire other constructor dependencies as well.
With the ASP.NET Core DI container, you can only hand-wire your services using a delegate. For instance:
services.AddTransient<MyService>(c => new MyService(
BuildLogger(typeof(MyService).Name),
c.GetRequiredService<ISomeOtherDependency>(),
c.GetRequiredService<IYetAnotherOne>());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With