Currently i am using ABP 3.3.0 and i have 2 DbContexts. One is a MSSQL context and the other is MySQL.
In order to get the connection strings, i have inherited from the DefaultConnectionStringResolver
class and overridden the GetNameOrConnectionString
method.
This works fine in Development, however whenever i try and run Unit Tests i get the following exception
Castle.MicroKernel.Handlers.HandlerException : Can't create component 'Portal.EntityFrameworkCore.CustomerDbConnectionStringResolver' as it has dependencies to be satisfied.
'Portal.EntityFrameworkCore.CustomerDbConnectionStringResolver' is waiting for the following dependencies: - Service 'Microsoft.AspNetCore.Hosting.IHostingEnvironment' which was not registered.
Which i understand is because i am injecting IHostingEnvironment
into the connection string resolver.
So my question is, if i cannot inject IHostingEnvironment
into the connection string resolver class, how else can i access the connection strings?
For clarity, in development the connection strings are stored as User Secrets. in Staging/Production they are stored in their respective appSettings.{EnvironmentName}.json file.
Here is the implementation of the connection string resolver class.
public class CustomerDbConnectionStringResolver : DefaultConnectionStringResolver
{
private readonly IHostingEnvironment _env;
public CustomerDbConnectionStringResolver(
IAbpStartupConfiguration configuration,
IHostingEnvironment env)
: base(configuration)
{
_env = env;
}
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
var configuration = AppConfigurations.Get(_env.ContentRootPath, _env.EnvironmentName,_env.IsDevelopment());
switch (args["DbContextType"].ToString())
{
case "Portal.EntityFrameworkCore.FreeRadiusDbContext":
return configuration.GetConnectionString(PortalConsts.FreeRadiusConnectionStringName);
default:
return configuration.GetConnectionString(PortalConsts.ConnectionStringName);
}
}
}
Many Thanks!
EDIT
Example of the current DBContextFactory.
public class FreeRadiusDbContextFactory : IDesignTimeDbContextFactory<FreeRadiusDbContext>
{
public FreeRadiusDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<FreeRadiusDbContext>();
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
FreeRadiusDbContextConfigurator.Configure(
builder,
configuration.GetConnectionString(PortalConsts.FreeRadiusConnectionStringName)
);
return new FreeRadiusDbContext(builder.Options);
}
}
@aaron ok so I think I have finally arrived at a solution that works in all environments.
Note: As for the double backslash issue, it looks like I had the connection string in an environment variable with the double backslash when it should have been a single backslash.
So here is the final CustomDbConnectionStringResolver
class.
public class CustomDbConnectionStringResolver : DefaultConnectionStringResolver
{
public CustomDbConnectionStringResolver(
IAbpStartupConfiguration configuration
)
: base(configuration)
{
}
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configuration = AppConfigurations.Get(Directory.GetCurrentDirectory(), environment, environment == "Development");
switch (args["DbContextType"].ToString())
{
case "Portal.EntityFrameworkCore.FreeRadiusDbContext":
return configuration.GetConnectionString(PortalConsts.FreeRadiusConnectionStringName);
default:
return configuration.GetConnectionString(PortalConsts.ConnectionStringName);
}
}
}
Which works quite well as its reusing code that already exists in the template, and still allows for different configurations to be loaded for the various environments.
Many thanks for your assistance @aaron!