121

I'm using vNext implementation of DI. How to pass parameters to constructor? For example, i have class:

public class RedisCacheProvider : ICacheProvider
{
    private readonly string _connectionString;

    public RedisCacheProvider(string connectionString)
    {
        _connectionString = connectionString;
    }
    //interface methods implementation...
}

And service register:

services.AddSingleton<ICacheProvider, RedisCacheProvider>();

How to pass parameter to constructor of RedisCacheProvider class? For example for Autofac:

builder.RegisterType<RedisCacheProvider>()
       .As<ICacheProvider>()
       .WithParameter("connectionString", "myPrettyLocalhost:6379");
Nkosi
  • 235,767
  • 35
  • 427
  • 472
Oleksandr Nahirniak
  • 1,357
  • 2
  • 9
  • 12
  • My generic solution: https://gist.github.com/ReallyLiri/c669c60db2109554d5ce47e03613a7a9 – Mugen Sep 18 '19 at 11:52
  • 1
    Related post - [.NET Core DI, ways of passing parameters to constructor](https://stackoverflow.com/q/53884417/465053) – RBT Sep 20 '21 at 11:40
  • Note that a connection string is generally considered configuration (and, in fact, often a secret because it may contain user names and passwords). So the "correct" way to handle this is to depend on IConfiguration and get it from there (e.g. using "redisCache::connectionString" as key). Then you just need to configure a value, either in appsettings.json, or in the project secrets (or in whatever other configuration store is in use). – Zastai Apr 13 '23 at 11:29

5 Answers5

174

You can either provide a delegate to manually instantiate your cache provider or directly provide an instance:

services.AddSingleton<ICacheProvider>(provider => new RedisCacheProvider("myPrettyLocalhost:6379"));

services.AddSingleton<ICacheProvider>(new RedisCacheProvider("myPrettyLocalhost:6379"));

Please note that the container will not explicitly dispose of manually instantiated types, even if they implement IDisposable. See the ASP.NET Core doc about Disposal of Services for more info.

Joe Ivans
  • 33
  • 6
Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
  • 1
    Simple and Useful – Himalaya Garg Oct 09 '18 at 07:23
  • 42
    Don't forget if your service takes other parameters you have registered, you can pass a reference to your service when it is registered. e.g. if "RedisCacheProvider" also required ISomeService, you'd do this: services.AddSingleton(provider => new RedisCacheProvider("myPrettyLocalhost:6379", provider.GetService())); – raterus Apr 14 '19 at 20:52
  • 3
    @KévinChalet it would be good if you would specify in your answer that "manually instantiated types" is *solely* about registering types through `AddSingleton(T)`. Types returned from registered delegates (e.g. using `AddSingleton(Func)`) will in fact be disposed of. – Steven Dec 18 '20 at 10:41
  • 2
    How do I go about handling this if I have another implementation of the ICacheProvider? – Ak777 Jul 08 '21 at 16:06
42

If the constructur also has dependencies that should be resolved by DI you can use that:

public class RedisCacheProvider : ICacheProvider
{
    private readonly string _connectionString;
    private readonly IMyInterface _myImplementation;

    public RedisCacheProvider(string connectionString, IMyInterface myImplementation)
    {
        _connectionString = connectionString;
        _myImplementation = myImplementation;
    }
    //interface methods implementation...
}

Startup.cs:

services.AddSingleton<IMyInterface, MyInterface>();
services.AddSingleton<ICacheProvider>(provider => 
    RedisCacheProvider("myPrettyLocalhost:6379", provider.GetService<IMyInterface>()));
feeeper
  • 2,865
  • 4
  • 28
  • 42
  • 1
    What if the class is not a singleton? Also, what if you can't directly instantiate the class because its constructor takes other parameters from DI? Something like `public class ManyObjects(SomeDbContext fromDI1, SomeClass fromDI2, string stringArg)`? The only way I can think of it is making a class like `class StringArgForManyObject` and add it as a singleton to the DI, but its cumbersome. Is there any better way? (the `stringArgs` is the same for all instances of `ManyObjects` within the app instance, but it can vary between different app instances). – Damn Vegetables Nov 23 '22 at 11:46
21

You can use :

 services.AddSingleton<ICacheProvider>(x =>
      ActivatorUtilities.CreateInstance<RedisCacheProvider>(x, "myPrettyLocalhost:6379"));

Dependency Injection : ActivatorUtilities will inject any dependencies to your class.

Here is the link to the MS docs: Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance

Also: See @poke's answer here for more information. Basically it pulls from the provided services and any other params you pass, like a composit constructor.

Joshua G
  • 2,086
  • 3
  • 19
  • 22
Henry Huangal
  • 211
  • 2
  • 2
12

You can use something like the example code below.

Manager class:

public class Manager : IManager
{
    ILogger _logger;
    IFactory _factory;
    public Manager(IFactory factory, ILogger<Manager> logger)
    {
        _logger = logger;
        _factory = factory;
    }
}

Startup.cs class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFactory, Factory>(sp =>
    {
        var logger = sp.GetRequiredService<ILogger<Factory>>();
        var dbContext = sp.GetRequiredService<MyDBContext>();
        return new Factory(dbContext, logger);
    });
    services.AddTransient<IManager, Manager>(sp =>
    {
        var factory = sp.GetRequiredService<IFactory>();
        var logger = sp.GetRequiredService<ILogger<Manager>>();
        return new Manager(factory, logger);
    });
}

You can read the full example here: DI in Startup.cs in .Net Core

Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
Rahul Jha
  • 874
  • 1
  • 11
  • 28
2

A bit late to the party, but you could DI inject a factory that creates and exposes an instance of your provider class.

Eric Johansson
  • 582
  • 2
  • 14