'With DI, create a new instance every time the interface is used

I have something like the code below.

The inyected class has a state, so I want to create a clean new instance of the class implementing INumberWriter every time it is called. Is there a way to do this?

public class NumberWriter() : INumberWriter{
   private int i = 1;
   public void Write(){
      ConsoleWrite(i++);
   }
}

public class SomeClass(){
   private INumberWriter writer;
   public SomeClass(INumberWriter writer){
      this.writer= writer;
   }

   public void AMethod(){
      for(int i=0;i<5;i++){
         writer.Write();// I want to see on console 11111 insted of 12345
      }
   }
}



Solution 1:[1]

This is not something that your DI Container can (or arguably should) solve for you. It's the DI Container's job to inject a dependency into a consumer. After that, the consumer stores the dependency in a private field, which means that -as long as the consumer lives- it will reuse that same dependency.

Fortunately, you are programming to interfaces, which means you can solve this problem using a special Proxy implementation that can be placed inside your application's Composition Root. For instance:

public class AlwaysNewNumberWriter : INumberWriter
{
    public void Write() => new NumberWriter().Write();
}

Instead of registering NumberWriter in the container, you can now register AlwaysNewNumberWriter as follows:

services.AddSingleton<INumberWriter, AlwaysNewNumberWriter>();

This solution, however, might be too simplistic, because NumberWriter might have dependencies of its own, that need to be resolved from the DI Container. In that case, you'll need a slightly more complex solution:

public class DispatchingNumberWriter : INumberWriter
    where TNumberWriter : INumberWriter
{
    private readonly Func<INumberWriter> factory;
    
    public DispatchingNumberWriter(Func<INumberWriter> factory)
    {
        this.factory = factory;
    }

    public void Write() => this.factory.Invoke().Write();
}

This DispatchingNumberWriter accepts a Func<INumberWriter> factory delegate, which it calls every time its Write method is called. This allows you to, instead of injecting the NumberWriter implementation into consumers, inject this DispatchingNumberWriter.

The following code demonstrates how to wire everything together:

services.AddTransient<NumberWriter>();

services.AddScoped<INumberWriter>(c =>
    new DispatchingNumberWriter(
        () => c.GetRequiredService<NumberWriter>()));

Please note the following about this last snippet:

  • The DispatchingNumberWriter is supplied with a delegate that calls back into the DI Container to resolve a new NumberWriter. This allows the NumberWriter to be created by the DI Container, which might construct the type using Auto-Wiring, i.e. automatically detecting and injecting its dependencies.
  • Because NumberWriter is created by the DI Container, it is registered as Transient; otherwise instances might still be reused.
  • The DispatchingNumberWriter is registered as Scoped, which the safest lifestyle for this class, because:
    • Registering DispatchingNumberWriter as Singleton will cause it's c parameter and the call to its GetRequiredService method to run in the container's global scope. This can cause issues in case NumberWriter or one of its transient dependencies start implementing IDisposable (which could result in a memory leak) or in case NumberWriter has direct or indirect dependencies with the Scoped lifestyle (which would make those dependencies Singletons).
    • Registering DispatchingNumberWriter as Transient will have the desired effect except in case it gets (accidentally) directly or indirectly injected into Singleton consumer. Because in that case you'll get the same behavior as described in the previous point with DispatchingNumberWriter registered as Singleton. Although the problem isn't different when injecting a Scoped DispatchingNumberWriter into a Singleton consumer, MS.DI prevents the injection of Scoped dependencies into Singleton consumers, because it is known pitfall to prevent.

Solution 2:[2]

When you need a new service on every iteration, you surely need to create a new one on every iteration. So there's no point in providing the dependency once upon creation of your SomeClass-instance, but upon every call to AMethod.

You may create some kind of factory to you class and let the factory create a new instance on each iteration:

public class WriterFactory
{
    public INumberWriter => new NumberWriter();
}

public class SomeClass
{
    private WriterFactory factory;
    public SomeClass(WriterFactory factory){
        this.factory = factory;
    }

    public void AMethod()
    {
        for(int i=0;i<5;i++)
        {
            var writer = this.factory().CreateNewWriter();
            writer.Write();
        }
    }
}

Now instead of providing a writer to your class, you provide a factory to the class which itself creates the writers.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Steven
Solution 2 MakePeaceGreatAgain