Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Services using Dependency Injection and Factory Pattern in ASP.NET

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?

like image 858
Hussein Salman Avatar asked Nov 09 '16 18:11

Hussein Salman


People also ask

Does dependency injection use Factory Pattern?

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.

Should services be singleton or transient?

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.

When should I use IServiceProvider?

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.


1 Answers

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>());
like image 85
Steven Avatar answered Nov 09 '22 08:11

Steven