1

I'm relatively new to ASP.NET Core MVC and am running into issues with dependency injection.

I have a solution with multiple projects in which I want to share a EF database context class. I have defined an interface for a configuration manager so I can share common config across projects, but have project specific config as well.

When running the various API's dependency injection fails with a

"System.InvalidOperationException: Unable to resolve service for type IConfigManager"

error.

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddl‌​eware[0] An unhandled exception has occurred while executing the request System.InvalidOperationException: Unable to resolve service for type 'NovaSec.Core.IConfigManager' while attempting to activate 'NovaSec.Core.Contexts.CustomDbContext'. at Microsoft.Extensions.DependencyInjection.ServiceLookup.Servi‌​ce.PopulateCallSites‌​(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)

The DBContextClass is part of a class library project I reference in the other projects.

I have no idea why it does not work. Can someone please help and explain this to me?

DBContext Class

   public class CustomDbContext : IdentityDbContext<CustomIdentity, CustomRole, string>
    {
        public CustomDbContext(DbContextOptions<CustomDbContext> options, IConfigManager configManager) : base(options)
        {
            var optionsBuilder = new DbContextOptionsBuilder<CustomDbContext>();
            optionsBuilder.UseSqlite(configManager._config.ConnectionStrings.FirstOrDefault(c => c.id == "IdentityDatabase").connectionString);
        }
    }

Config Manager interface and implementation class

public interface IConfigManager
{
    IAppConfig _config { get; set; }
}

public class ConfigManager : IConfigManager
{
    public IAppConfig _config { get; set; }
    public ConfigManager(IAppConfig config)
    {

    }
}

Startup Method

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConfigManager, ConfigManager>(config =>
    {
        return new ConfigManager(_config);
    });
    IServiceProvider serviceProvider = services.BuildServiceProvider();
    _configManager = (ConfigManager)serviceProvider.GetService<IConfigManager>();
    services.AddDbContext<CustomDbContext>();
    services.AddIdentity<CustomIdentity, CustomRole>(config => {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddEntityFrameworkStores<CustomDbContext>()
        .AddDefaultTokenProviders();
    services.AddIdentityServer()
    .AddInMemoryClients(_configManager.GetClients())
    .AddInMemoryIdentityResources(_configManager.GetIdentityResources())
    .AddInMemoryApiResources(_configManager.GetApiResources())
    .AddTemporarySigningCredential()
    .AddAspNetIdentity<CustomIdentity>();

}
2
  • Does that even compile? provide a minimal reproducible example that can be used to reproduce the problem. Commented Aug 12, 2017 at 16:25
  • You don't build the service provider inside the config method, and you don't add services after building the provider. Commented Aug 12, 2017 at 16:26

2 Answers 2

2

At this stage you are better off creating the manager manually, using it for the configuration and then registering it with the service collection.

Update context

public class CustomDbContext : IdentityDbContext<CustomIdentity, CustomRole, string> {
    public CustomDbContext(DbContextOptions<CustomDbContext> options) : base(options) { }
}

You should also configure the context in the startup.

public void ConfigureServices(IServiceCollection services) {
    var _configManager = new ConfigManager(_config); //Create new instance
    services.AddSingleton<IConfigManager>(provider => _configManager); // add as singleton

    services.AddDbContext<CustomDbContext>(options => 
        options.UseSqlite(_configManager._config.ConnectionStrings.FirstOrDefault(c => c.id == "IdentityDatabase").connectionString)
    );

    services.AddIdentity<CustomIdentity, CustomRole>(config => {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddEntityFrameworkStores<CustomDbContext>()
        .AddDefaultTokenProviders();

    services.AddIdentityServer()
        .AddInMemoryClients(_configManager.GetClients())
        .AddInMemoryIdentityResources(_configManager.GetIdentityResources())
        .AddInMemoryApiResources(_configManager.GetApiResources())
        .AddTemporarySigningCredential()
        .AddAspNetIdentity<CustomIdentity>();

}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! I tried that as well. It throws the same error: fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0] An unhandled exception has occurred while executing the request System.InvalidOperationException: Unable to resolve service for type 'NovaSec.Core.IConfigManager' while attempting to activate 'NovaSec.Core.Contexts.CustomDbContext'. at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
When I call any Controller on my application. If I simply configure DBContextClass directly in the ConfigureServices like services.AddDbContext<CustomDbContext>(options => options.UseSqlite("Data Source=idenitysource.db")) it all works fine. It seems to be related to the CustomDbContext class constructor not receiving the configManager object.
Hi Nkosi, I have it working with the code you posted. This is similar to what I started with. I tried to abstract away the dbContext so I can easily plugin another provider later. It's getting late here... code is dancing across the screen. Will check further tomorrow. Thanks a lot for helping me! Much appreciated!
@MarkPriem glad to help. if this answers your question mark it as the answer and vote if it was helpful.
0

Ok I finally got it. The final result is:

DbContext class

public class CustomDbContext : IdentityDbContext<CustomIdentity, CustomRole, string>
{
    private readonly IConfigManager _configManager;
    public CustomDbContext(DbContextOptions<CustomDbContext> options, IConfigManager configManager) : base(options)
    {
        this._configManager = configManager;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite(_configManager._config.ConnectionStrings.FirstOrDefault(c => c.id == "IdentityDatabase").connectionString);
    }
}

Startup

public IConfigurationRoot Configuration { get; set; }
public ConfigManager ConfigManager { get; set; }
public AppConfig Config { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json")
   .AddEnvironmentVariables();

    this.Configuration = builder.Build();
    this.Config = new AppConfig();
    this.Configuration.Bind(this.Config);
    this.ConfigManager = new ConfigManager(this.Config);
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConfigManager, ConfigManager>(provider => this.ConfigManager);
    services.AddDbContext<CustomDbContext>();
    services.AddIdentity<CustomIdentity, CustomRole>(config => {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddEntityFrameworkStores<CustomDbContext>()
        .AddDefaultTokenProviders();
    services.AddIdentityServer()
    .AddInMemoryClients(this.ConfigManager.GetClients())
    .AddInMemoryIdentityResources(this.ConfigManager.GetIdentityResources())
    .AddInMemoryApiResources(this.ConfigManager.GetApiResources())
    .AddTemporarySigningCredential()
    .AddAspNetIdentity<CustomIdentity>();
}

ConfigManager

public interface IConfigManager
{
    IAppConfig _config { get; set; }
}

public class ConfigManager : IConfigManager
    {
        public IAppConfig _config { get; set; }
        public ConfigManager(IAppConfig config)
        {
            this._config = config;
        }

    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.