dependency-injectionwindows-services.net-framework-versionlight-inject

.net framework- how to create IServiceProvider to get already registered service instance using IServiceProvider?


On .NET Framework 4.6.2 application, where there is no built-in DI container we are using LightInject DI Container to object initialization but don't know how to create 'IServiceProvider' Object in Main() so the other class implementations can get the already registered instance of service via IServiceProvider without using new keyword.

How to create IServiceProvider object? in .net framework 4.6.2 application

public class Program
{       
    public static void Main()
    {
        var container = new ServiceContainer();

        // calls below extension method
        container.RegisterDependencies();
    }
}

public static class LightInjectDIExtension
{        
    /// Registers the application dependencies.        
    public static void RegisterDependencies(this ServiceContainer container)
    {
        container.Register<IBackgroundService1, BackgroundService1>();
        container.Register<Service2>();
    }
}

Once IServiceProvider instance is available to use, I'm intended to do the below

// This is background service app & this class will be 
// instantiated once in application lifetime 
public class BackgroundService1 : IBackgroundService1
{
    private readonly IServiceProvider _serviceProvider;
    public BackgroundService1(IServiceProvider serviceProvider)
    {
       _serviceProvider = serviceProvider;
    }

    public void Method1(string elementName)
    {
        // every time call to 'Method1' has to get the new instance
        // of registered 'Service2' class rather than using 'new'
        // keyword
        var service2 =  (Service2)_serviceProvider.GetService(typeof(Service2)); 
        service2.CallMe();
    }
 }

Modification after Steven's suggestion

 public class BackgroundService1 : IBackgroundService1
{
    private readonly IServiceContainer_container;
    public BackgroundService1 (IServiceContainer container)
  //Exception thrown: 'System.InvalidOperationException' in LightInject.dll
    {
       _container = container;
    }

    public void Method1(string elementName)
    {
        // every time call to 'Method1' has to get the new instance
        // of registered 'Service2' class rather than using 'new'
        // keyword
        var service2 =  (Service2)_container.GetInstance(typeof(Service2)); 
        service2.CallMe();
    }
 }

Solution

  • In general, injecting an IServiceProvider (or any abstraction that gives access to an unbound set of dependencies is a bad idea, because it can lead to the Service Locator anti-pattern. A discussion on this anti-pattern can be found here.

    A Service Locator is something that only exists outside the Composition Root. Your BackgroundService1, however, might be part of the Composition Root, which might injecting a DI Container -or an abstraction there over- a feasible solution. Note that you should strive keeping all business logic out of the Composition Root. This ensures that BackgroundService1 purely functions as a mechanical peace of code that delegates the operation to classes that run the actual business logic.

    Though, when operating inside the Composition Root, there is typically no need to use an abstraction over your DI Container, such as an IServiceProvider. The Composition Root already has intrinsic knowledge over all application's dependencies, including your DI Container.

    This means that you can inject LightInject's ServiceContainer directly into the constructor of BackgroundService1; there is no need for an IServiceProvider.

    If, however, you insist in using the IServiceProvider abstraction, you can create an IServiceProvider implementation that wraps ServiceContainer and forwards its GetService method to the wrapped ServiceContainer. This wrapper class can than be registered in the ServiceContainer.