0

I am using DI in my dot net console application. I have an interface:

public interface IMainAction
{
    void Run();
} 

and 2 classes implementing this Interface.

I have configured my DI host like this:

static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureServices((context, services) =>
        {
            services.AddTransient<IMainAction, CityStoreClass>();
            services.AddTransient<IMainAction, ProvinceService>();
            services.AddSingleton<App>();
        }).UseSerilog();
}

My app class :

public class App
{
    private IMainAction _cityService;
    private IMainAction _ProvinceService;
    private readonly ILogger<CityStoreClass> _log;
    private readonly IConfiguration _config;
    
    public App(IMainAction cityService, IMainAction provinceService, ILogger<CityStoreClass> log, IConfiguration config)
    {
        _cityService = cityService;
        _ProvinceService = provinceService;
        _log = log;
        _config = config;
    }

    public void Run(string[] args)
    {
        Log.Logger.Information("App Run Starts");
        var connectionString = _config.GetValue<string>("ConnectionString");
        _log.LogInformation($"{connectionString}");

        ((CityStoreClass)_cityService).Run();
    }
}

I want to call Run method of _cityService class, but I got this error:

Unable to cast object of type 'EazyCityCA.Services.ProvinceService' to type 'EazyCityCA.Services.CityStoreClass'.

What is the problem?

Chris F Carroll
  • 11,146
  • 3
  • 53
  • 61
Cyrus the Great
  • 5,145
  • 5
  • 68
  • 149
  • 1
    IF you register the **same** interface twice, the **last** registration will be the one that's valid - so in your case, for `IMainService`, you get back a `ProvinceService` - not the `CItyStoreClass` you seem to expect ... – marc_s Apr 21 '23 at 12:07
  • 1
    I am tempted to rename the question to, How to get specific implementation out of .Net DI container? The question is more about how to work with the DI container than about casting anything? – Chris F Carroll Apr 21 '23 at 13:45

2 Answers2

3

What you've done is not how DI containers and dependency management are intended to be used.

When your public void Run(string[] args) method references

((CityStoreClass)_cityService)

this tells us that what the code really depends on is a CityStoreClass not a IMainAction. Otherwise you wouldn't need the cast.

If that's what you depend on, say so. Tell your DI container that you depend on a CityStoreClass:

    .ConfigureServices((context, services) =>
    {
        services.AddTransient<CityStoreClass>();
        services.AddTransient<ProvinceService>();
        services.AddSingleton<App>();
    })

Your interface IMainAction looks to be pointless? In which case you should remove it.

You may have learned, ‘but I should depend on abstractions like interfaces, not implementation classes!’ Yes, but the abstraction is supposed to abstract “What a CityStore does” not just abstract “a callable method”. If you can't see the point of a CityStore abstraction, then don't use one. When you do see a point, call it ICityStore to show specifically what it abstracts.

Either way: tell the DI container what your App really depends on, then the DI can provide exactly that.

Chris F Carroll
  • 11,146
  • 3
  • 53
  • 61
1

You're expecting multiple registrations for the same interface to be resolved on... registration order? Parameter name matching the implementation type?

But that's not how it works.

If you resolve a single service (IMainAction foo) as opposed to all registered services (IEnumerable<IMainAction>), you get the last one registered. It doesn't matter that you resolve a single service twice: IMainAction cityService, IMainAction provinceService just resolves the same IMainAction twice.

You could for example:

  • Use a third-party container and use named resolution.
  • Register and resolve them by implementation (services.AddTransient<CityStoreClass>()).
  • Resolve and run them all (IEnumerable<IMainAction> actions, foreach (var action in actions) action.Run().
CodeCaster
  • 147,647
  • 23
  • 218
  • 272