I configure my database context by allowing changing of the connectionString based on the users cookie:
services.AddDbContext<HotdogContext>((serviceProvider, dbContextBuilder) =>
{
string connectionString = "DatabaseUS"; // Default connection string
var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
var dbNameCookie = httpContextAccessor.HttpContext.Request.Cookies.ContainsKey("country");
if (dbNameCookie) // debug
{
Console.Write("Country cookie exists!");
}
var country = httpContextAccessor.HttpContext.Request.Cookies["country"]?.ToString() ?? "null";
if (country.Equals("null"))
{
Console.WriteLine("Country cookie is null");
}
else if (country.Equals("USA"))
{
Console.WriteLine("Set USA connection string");
connectionString = "DatabaseUS";
}
else if (country.Equals("AUS"))
{
connectionString = "DatabaseAUS";
Console.WriteLine("Set Australia connection string");
}
else if (country.Equals("S.A."))
{
//connectionString = "DatabaseSA";
Console.WriteLine("Set South America connection string");
}
dbContextBuilder.UseSqlServer(Configuration.GetConnectionString(connectionString));
});
This clutters the startup.cs if I add additional databaseContexts to the program. Is there a way I can hide this logic from startup.cs and use extension methods?
services.AddDbContext<HotdogContext>((serviceProvider, dbContextBuilder) =>
{
string connectionString = "DatabaseUS"; // Default connection string
var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
var country = dbContextBuilder.GetRequestedCountry(httpContextAccessor); // Extension method
if(country !=null) connectionString = country;
dbContextBuilder.UseSqlServer(Configuration.GetConnectionString(connectionString));
});
CodePudding user response:
One way is to define a service interface, then implement with your logic:
public interface IConnectionStringNameService
{
string GetName();
}
public class ConnectionStringNameFromCookieService
: IConnectionStringNameService
{
private readonly HttpContext _httpContext;
public ConnectionStringNameFromCookieService(
HttpContextAccessor httpContextAccessor )
{
_httpContext = httpContextAccessor.HttpContext;
}
public string GetName()
{
var country = _httpContext.Request
.Cookies[ "country" ]
?? "default value"; // or throw exception
return country switch =>
{
"USA" => "DatabaseUS",
"AUS" => "DatabaseAUS",
"S.A." => "DatabaseSA",
_ => "Default name or throw",
}
}
}
Then you register the service implementation with the container:
services.AddScoped<IConnectionStringNameService, ConnectionStringNameFromCookieService>();
Then you can resolve this service from the service provider when you need need the connection string name:
var connStrNameSvc = serviceProvider.GetRequiredService<IConnectionStringNameService>.();
var connStrName = connStrNameSvc.GetName();
In place (in the startup file), the above would abstract to:
services.AddDbContext<HotdogContext>(
(serviceProvider, dbContextBuilder) =>
{
var connStrName = serviceProvider
.GetRequiredService<IConnectionStringNameService>()
.GetName();
dbContextBuilder.UseSqlServer(
Configuration.GetConnectionString( connStrName ) );
});
But you can then use an extension method to abstract this call to a single, simple line:
public class BlahBlahExtensionMethods
{
// come up with a better name
public IServiceCollection AddDbContextUsingCookieForConnStrName<TDbContext>(
this IServiceCollection services )
where TDbContext : DbContext
{
return services.AddDbContext<TDbContext>(
( serviceProvider, dbContextBuilder ) =>
{
var connStrName = serviceProvider
.GetRequiredService<IConnectionStringNameService>()
.GetName();
dbContextBuilder.UseSqlServer(
Configuration.GetConnectionString( connStrName ) );
});
}
}
Then in startup you'd only need:
services.AddDbContextUsingCookieForConnStrName<HotdogContext>();
Or you can inject the name provider into HotdogContext
and use it in an OnConfiguring( DbContextOptionsBuilder )
method override.