unable to run API controller with dbcontext injection - c#

i get following error:
An unhandled exception occurred while processing the request.
InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
my code is as follows
Api controller program file
using BL;
using DAL;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<PokerContext>(options =>
options.UseSqlServer(builder.Configuration
.GetConnectionString("PokerConnex"))
.EnableSensitiveDataLogging()
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
builder.Services.AddScoped<Logic>();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
public partial class Program { }
Context file
public class PokerContext: DbContext
{
public PokerContext()
{
}
public PokerContext(DbContextOptions<PokerContext> options)
: base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Attendee> Attendees { get; set; }
public DbSet<Event> Events { get; set; }
//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//{
// optionsBuilder.UseSqlServer(
// "Data Source= (localdb)\\MSSQLLocalDB; Initial Catalog=PokerAppData")
// .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information)
// .EnableSensitiveDataLogging();
//}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PokerContext>(options => options.UseSqlServer("Data Source= (localdb)\\MSSQLLocalDB; Initial Catalog=PokerTestAppData"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
Logic file (created a parameterless constructor here
public class Logic
{
Repository _repo;
public Logic()
{
_repo = new Repository();
}
public Logic(Repository repo)
{
_repo = repo;
}
}
Yet when i try to run the api in browser i always get the same error
any pointers?

hmmm okay, uncommenting the Onconfiguring, and adding an if check for isconfigured fixed it
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(
$"Data Source= (localdb)\\MSSQLLocalDB; Initial Catalog={appCatalog}")
.LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information)
.EnableSensitiveDataLogging();
}
}

Related

System.InvalidOperationException: Unable to resolve service in ASP.NET Core

I am a beginner in ASP.NET Core. I am creating a Web API service. While I am fetching the data from the database, I had a problem. What is the error I got? I have successfully done the database migration part and created the database successfully.
System.InvalidOperationException: Unable to resolve service for type 'webb.StudentDbContext' while attempting to activate 'webb.Controllers.StudentController'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method3(Closure , IServiceProvider , Object[] )
StudentController:
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private StudentDbContext studentDbContext;
public StudentController(StudentDbContext studentDbContext)
{
studentDbContext = studentDbContext;
}
// GET: api/<EmployeeController>
[HttpGet]
public IEnumerable<Student> Get()
{
return studentDbContext.Student;
}
}
Model class:
namespace webb.Model
{
public class Student
{
public int id { get; set; }
public int stname { get; set; }
public int course { get; set; }
}
}
StudentDbContext:
public class StudentDbContext : DbContext
{
public DbSet<Student> Student { get; set; }
public StudentDbContext()
{
}
public StudentDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasKey(e => e.id);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=.;Initial Catalog=sms;Integrated Security=True; TrustServerCertificate = True");
}
}
appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"StudentConnStr": "Data Source=.;Initial Catalog=sms;Integrated Security=True;"
}
}
Where I am going to set the key.
Program.cs:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using webb;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
builder.Services.AddDbContext<StudentDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("StudentDbContext")));
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Look like you are missing out inject StudentDbContext which is the DbContext to DI container.
Add below to your Program.cs:
builder.Services.AddDbContext<StudentDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("StudentConnStr")));
refer to this and Part 5, work with a database in an ASP.NET Core MVC app
Update
builder.Services.AddDbContext<StudentDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("StudentConnStr")));
var app = builder.Build();
builder.Build() should be after all AddXXX functions. In other words, you need to swap it with AddDbContext()
builder.Services.AddDbContext<StudentDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("StudentDbContext")));
var app = builder.Build();

IConfiguration returning null. Cannot Read from Database

I have a .Net Core 3.1 Program returning the error
System.ArgumentNullException: 'Value cannot be null. (Parameter 'connectionString')'
when I try to login to the system.
I have set a breakpoint onto
public IConfiguration Configuration { get; }
On building,the connection string can be seen but immediately I login it returns null.
Below is my code.
Startup.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSession();
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Login/Login", "");
});
services.AddDbContext<CENTREContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")));
//var connection = Configuration.GetConnectionString("ConnectionStrings");
//services.Configure<ConnectionStrings>(Configuration.GetSection("DatabaseConnection"));
//services.AddDbContext<CENTREContext>(options => options.UseSqlServer(connection));
}
ContextClass
public CENTREContext(DbContextOptions<CENTREContext> options)
: base(options)
{
}
public CENTREContext()
{ }
public IConfiguration Configuration { get; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Configuration.
GetConnectionString("ConnectionStrings:DatabaseConnection"));
}
}
appsettings.json
{
"ConnectionStrings": {
"DatabaseConnection": "Server=xx-xx;Database=CENTRE;UID=xx;Password=xxx;"
},
"AllowedHosts": "*"
}
I have tried but nothing seems to work.
Help on the same will be appreciated.
You have mixed your options.
GetSection + index operator
connectionStrings = configuration.GetSection("ConnectionStrings");
connectionString = connectionStrings["DatabaseConnection"];
GetSection + path
connectionString = configuration.GetSection("ConnectionStrings:DatabaseConnection").Value;
GetConnectionString
connectionString = configuration.GetConnectionString("DatabaseConnection");

How do I set up a connection string into appsettings.json?

How can I set up a connection string in my ASP.NET Core blazor WebAssembly Server component where I created the appsettings.json.
{
"ConnectionStrings": {
"SQLiteTestConnection": "Data Source=./TestDB.db",
}
}
Right now it looks like this but I cant create the database via Update-Database.
Startup.cs:
...
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
// Adding the DbContext references
services.AddDbContext<SQLiteTestDbContext>(options =>
options.UseSqlite("./TestDB.db"));
}
...
my DbContext which is in use.
This DbContext is stored in my Blazor Server component
using DB_SQLite;
using DB_SQLite.SQL_Models;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace BlazorWeb.Server.Data
{
public class SQLiteTestDbContext : DbContext
{
#region Constructor
// Default parameterless Constructor
public SQLiteTestDbContext(DbContextOptions options)
: base(options)
{
}
#endregion
public DbSet<ObjectModel> Objects { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=./TestDB.db");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region Configure Object
modelBuilder.Entity<ObjectModel>().HasData(LoadObjects());
base.OnModelCreating(modelBuilder);
#endregion
}
#region Seeding
private List<ObjectModel> LoadObjects()
{
return new List<ObjectModel>
{
new ObjectModel() { Id = 1, Name = "Schraube", TagName = "Werkzeug" ,PreviewImage = "null"},
new ObjectModel() { Id = 2, Name = "Gabelstapler", TagName = "Fahrzeug" ,PreviewImage = "null"},
new ObjectModel() { Id = 3, Name = "Zange", TagName = "Werkzeug" , PreviewImage = "null"},
new ObjectModel() { Id = 4, Name = "Sechskantschraube", TagName = "Werkzeug", PreviewImage = "null"},
};
}
#endregion
}
}
Im also creating some fake data into the database in the DbContext Class.
In your Startup.cs class declare an instance of IConfiguration as a field and initialize it in the constructor.
public class Startup
{
private IConfiguration Configuration { get; }
public Startup()
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, false);
Configuration = configurationBuilder.Build();
}
// Class continues
}
Then in your ConfigureServices() method, you can use the following to declare your IConfiguration instance as a singleton service, which allows you to inject it and use it other classes.
services.AddSingleton(Configuration);
You actually do not need to specify your database connection string in your DbContext class since you have specified it in your service collection.
So in your Startup.cs, you would now do
services.AddDbContext<SQLiteTestDbContext>
(options => options.UseSqlite(Configuration["ConnectionStrings:SQLiteTestConnection"]));
You may need to reference the following packages
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json

Overriding database provider in integration test with WebApplicationFactory

I am following the official MS documentation for integration testing .Net Core (https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1).
I was able to get the first part of the integration test done where I was not overriding the startup class of the application I am testing (i.e. I was using a web application factorythat did not override any services).
I want to override the database setup to use an in-memory database for the integration test. The problem I am running into is that the configuration continues to try and use the sql server for services.AddHangfire().
How do I override only above specific item in my integration test? I only want to override the AddHangfire setup and not services.AddScoped<ISendEmail, SendEmail>(). Any help would be appreciated.
Test Class with the custom web application factory
public class HomeControllerShouldCustomFactory : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public HomeControllerShouldCustomFactory(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync("/Home/Index");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Send Email", responseString);
}
}
Custom Web Application Factory
public class CustomWebApplicationFactory<TStartup>: WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
My startup.cs in my application that I am testing
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<ISendEmail, SendEmail>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHangfireServer();
app.UseHangfireDashboard();
RecurringJob.AddOrUpdate<ISendEmail>((email) => email.SendReminder(), Cron.Daily);
app.UseMvc();
Update
I don't see this issue in my other example project where I am using only entity framework. I have a simple application with an application db context which uses SQL server. In my test class, I override it with an in-memory database and everything works. I am at a loss at to why it will work in my example application but not work in my main application. Is this something to do with how HangFire works?
In my test application (example code below), I can delete my sql database, run my test, and the test passes because the application DB context does not go looking for the sql server instance but uses the in-memory database. In my application, the HangFire service keeps trying to use the sql server database (if I delete the database and try to use an in-memory database for the test - it fails because it can't find the instance its trying to connect to). How come there is such a drastic difference in how the two projects work when a similar path is used for both?
I ran through the debugger for my integration test which calls the index method on the home controller above (using the CustomWebApplicationFactory). As I am initializing a test server, it goes through my startup class which calls below in ConfigureServices:
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
After that, the Configure method tries to call below statement:
app.UseHangfireServer();
At this point the test fails as It cannot find the DB. The DB is hosted on Azure so I am trying to replace it with an in-memory server for some of the integration test. Is the approach I am taking incorrect?
My example application where its working
Application DB Context in my example application
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Message> Messages { get; set; }
public async Task<List<Message>> GetMessagesAsync()
{
return await Messages
.OrderBy(message => message.Text)
.AsNoTracking()
.ToListAsync();
}
public void Initialize()
{
Messages.AddRange(GetSeedingMessages());
SaveChanges();
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "You're standing on my scarf." },
new Message(){ Text = "Would you like a jelly baby?" },
new Message(){ Text = "To the rational mind, nothing is inexplicable; only unexplained." }
};
}
}
Startup.cs in my example application
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
CustomWebApplicationFactory - in my unit test project
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
My unit test in my unit test project
public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public UnitTest1(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async System.Threading.Tasks.Task Test1Async()
{
var response = await _client.GetAsync("/");
//response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Home", responseString);
}
Update 2
I think I found an alternate to trying to override all my configuration in my integration test class. Since it's a lot more complicated to override HangFire as opposed to an ApplicationDBContext, I came up with below approach:
Startup.cs
if (Environment.IsDevelopment())
{
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
}
else
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration["DBConnection"]));
}
Then in my CustomWebApplicationBuilder, I override the environment type for testing:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development"); //change to Production for alternate test
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
});
}
}
With that approach, I don't need to worry about having to do extra logic to satisfy hangfire's check for an active DB. It works but I am not 100% convinced its the best approach as I'm introducing branching in my production startup class.
There are two different scenarios you need to check.
Create a job by class BackgroundJob
Create a job by interface IBackgroundJobClient
For the first option, you could not replace the SqlServerStorage with MemoryStorage.
For UseSqlServerStorage, it will reset JobStorage by SqlServerStorage.
public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(
[NotNull] this IGlobalConfiguration configuration,
[NotNull] string nameOrConnectionString)
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (nameOrConnectionString == null) throw new ArgumentNullException(nameof(nameOrConnectionString));
var storage = new SqlServerStorage(nameOrConnectionString);
return configuration.UseStorage(storage);
}
UseStorage
public static class GlobalConfigurationExtensions
{
public static IGlobalConfiguration<TStorage> UseStorage<TStorage>(
[NotNull] this IGlobalConfiguration configuration,
[NotNull] TStorage storage)
where TStorage : JobStorage
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (storage == null) throw new ArgumentNullException(nameof(storage));
return configuration.Use(storage, x => JobStorage.Current = x);
}
Which means, no matter what you set in CustomWebApplicationFactory, UseSqlServerStorage will reset BackgroundJob with SqlServerStorage.
For second option, it could replace IBackgroundJobClient with MemoryStorage by
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddSingleton<JobStorage>(x =>
{
return GlobalConfiguration.Configuration.UseMemoryStorage();
});
});
}
}
In conclusion, I suggest you register IBackgroundJobClient and try the second option to achieve your requirement.
Update1
For DB is not available, it could not be resolved by configuring the Dependency Injection. This error is caused by calling services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));.
For resolving this error, you need to overriding this code in Startup.cs.
Try steps below:
Change Startup to below:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//Rest Code
ConfigureHangfire(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Rest Code
app.UseHangfireServer();
RecurringJob.AddOrUpdate(() => Console.WriteLine("RecurringJob!"), Cron.Minutely);
}
protected virtual void ConfigureHangfire(IServiceCollection services)
{
services.AddHangfire(config =>
config.UseSqlServerStorage(Configuration.GetConnectionString("HangfireConnection"))
);
}
}
Create StartupTest in test project.
public class StartupTest : Startup
{
public StartupTest(IConfiguration configuration) :base(configuration)
{
}
protected override void ConfigureHangfire(IServiceCollection services)
{
services.AddHangfire(x => x.UseMemoryStorage());
}
}
CustomWebApplicationFactory
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint: class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder(null)
.UseStartup<TEntryPoint>();
}
}
Test
public class HangfireStorageStartupTest : IClassFixture<CustomWebApplicationFactory<StartupTest>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<StartupTest> _factory;
public HangfireStorageStartupTest(CustomWebApplicationFactory<StartupTest> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
}

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

Categories