Mark Priem
Mark Priem

Reputation: 397

ASP.NET Core MVC Dependency Injection issue

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>();

}

Upvotes: 1

Views: 1802

Answers (2)

Mark Priem
Mark Priem

Reputation: 397

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;
        }

    }
}

Upvotes: 0

Nkosi
Nkosi

Reputation: 247531

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>();

}

Upvotes: 2

Related Questions