问题
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?
回答1:
There are a couple of errors in your approach:
- Your services depend on the concrete
LoggerFactory
type which is a Dependency Inversion Principle violation. - Doing this extra initialization can make building the object graph unreliable, while injection constructors should be simple.
- It hides the fact that the
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. - The use of a factory is a smell, since factories are hardly ever the right solution.
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>());
来源:https://stackoverflow.com/questions/40513388/passing-services-using-dependency-injection-and-factory-pattern-in-asp-net