ASP Core 3.1 multiple/alterante DB contexts - c#

I have a question, about how to set up a project for multiple clients. We currently develop an ASP Core application. All clients use the same database structure, so they have identical databases, just filled with different users,... We currently use one publish, set up two test websites on IIS and each of those publishes has a different JSON config file containing the DB context (read out at Startp.cs).
Our problem here is, that if we have multiple clients, we have to copy our publish multiple times to maintain multiple websites for the IIS. We didn't find a way to just use one publish and define the config file to be used dependent on the URL/port of the connected client. We tried a tutorial for tenancy under ASP Core, but it failed at the User Manager.
Can someone point me out, to what's the best solution for this kind of webproject, which requires one shared website/publish under IIS, different DB contexts for each client (URL/port), user authentication (we used Identity for that). We tried this for weeks now, but failed to get it working. Our main problem is, that the DB context is created in the Startup.cs before the host is built, so we don't have any URL's from the request and can't create a specific DB context when the application starts. This means, we can't fill the UserStore and can't get the login to work. We know we could make a master table with all the users and domains, but we really would like to avoid this approach, as we are not sure, if the websites will run on the same server, or if one client may use his own internal company server.
EDIT: I need to be more specific, as I made a small mistake. The databases are identical. So i actually won't need different context classes, but just different connection strings. But they are set in the Startup.cs in the AddDbContext function...
EDIT2: I tried a few solutions now, and I'm always failing on Identity. It doesn't seem to work and crashes, when no DbContext is created directly during Startup.
EDIT3: As I failed so far, here are some Code Snippets from our Projekt
In our Startup.cs/ConfigureServices(IServiceCollection services)
We are loading our DB settings from a config file
IConfigurationSection DbSection = Configuration.GetSection("Database");
We are adding the DB Context
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(MyDbContext.createConnectionString(DbSection), b => b.MigrationsAssembly("MyNamespace.DB").EnableRetryOnFailure()));
And setting up Identity
services.AddScoped<Microsoft.AspNetCore.Identity.SignInManager<MyUser>, MySignInManager>();
services.AddScoped<Microsoft.AspNetCore.Identity.IUserStore<MyUser>, MyUserStore>();
services.AddScoped<Microsoft.AspNetCore.Identity.UserManager<MyUser>, Services.LogIn.MyUserManager>();
services.AddIdentity<MyUser, MyRole>()
.AddUserManager<MyUserManager>()
.AddUserStore<MyUserStore>()
.AddRoleStore<MyRoleStore>()
.AddDefaultTokenProviders();
We are adding authorization
services.AddAuthorization(options =>
{ options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
In our Startup.cs/Configure(...)
- We set UseAuthorization
app.UseAuthentication();
app.UseAuthorization();
In our MyDbContext class we are creating the connection string
public static string createConnectionString(IConfigurationSection configurationSection)
{
return #"server=" + configurationSection.GetSection("Server").Value +
";database=" + configurationSection.GetSection("Database").Value +
";persist security info=True;user id=" + configurationSection.GetSection("User").Value +
";password=" + configurationSection.GetSection("Password").Value + ";multipleactiveresultsets=True;";
}
So basically all we are trying to achieve, is to make different connections for different urls. Let's say we have 2 cients:
clientone.mysite.com:123 -> database_clientone
clienttwo.mysite.com:456 -> database_clienttwo
This does not work, as we don't have the domain when creating the DbContext. But we are required to have each database store it's own login credentials, instead of using a master table with all the logins for each existing users/databases...
And Identity doesn't seem to work with this scenario either.

If you are registering your DB context with Core's DI via AddDbContext call you can leverage it's overload which provides you access to IServiceProvider:
public void ConfigureServices(IServiceCollection services)
{
services
.AddEntityFrameworkSqlServer()
.AddDbContext<MyContext>((serviceProvider, options) =>
{
var connectionString = serviceProvider.GetService // get your service
// which can determine needed connection string based on your logic
.GetConnectionString();
options.UseSqlServer(connectionString);
});
}
Also here something similar is achieved with Autofac.

You can have multiple appsettings.json files and depending on client load appropriate json file .
With above case you will still be creating Db
Context in Startup.cs
public class ConfigurationService : IConfigurationService
{
public IConfiguration GetConfiguration()
{
var env = GetCurrentClient();//You can also set environment variable for client
CurrentDirectory = CurrentDirectory ?? Directory.GetCurrentDirectory();
return new ConfigurationBuilder()
.SetBasePath(CurrentDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: false)
.AddEnvironmentVariables()
.Build();
}
}

Instead of using AddDbContext , try to use inject DBContext lazily.
Add a class DataContext which will implement DBContext
public class DataContext : DbContext
{
public DataContext(IConfigurationService configService)
: base(GetOptions( GetConnectionString())
{
this.ChangeTracker.LazyLoadingEnabled = true;
}
private static DbContextOptions GetOptions(string connectionString)
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}
public static string GetConnectionString()
{
//Your logic to read connection string by host
// This method will be called while resolving your repository
}
}
In your Startup.cs
remove AddDbContext() call
and instead use
public void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddScoped<DbContext, YourDBContext>();
//likewise Addscope for your repositories and other services
//Code to build your host
}
DI DbContext in your controller's constructor or in service's constructor
Since your DBContext is now being loaded lazily , you will be able decide on connection string depending on your host.
Hope this works for you!!

I tried to create an environment variable via web.config and reading it out within our application. But I couldn't find a way, how to setup multiple websites in IIS7 with the SAME publish/file-folder, but DIFFERENT web.config files. This didn't work either, or at least I had no clue how to achieve this...

Related

ASP.NET Core Integration tests with dotnet-testcontainers

I am trying to add some integration tests for a aspnetcore v6 webapi following the docs - https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0#aspnet-core-integration-tests.
My webapi database is SQLServer. I want the tests to be run against an actual SQLServer db and not in-memory database. I came across dotnet-testcontainers - https://github.com/HofmeisterAn/dotnet-testcontainers and thinking of using this so I do not need to worry about the resetting the db as the container is removed once test is run.
So this is what I plan to do:
Start-up a SQLServer testcontainer before the test web host is started. In this case, the test web host is started using WebApplicationFactory. So the started wen host has a db to connect with. Otherwise the service start will fail.
Run the test. The test would add some test data before its run.
Then remove the SQLServer test container along with the Disposing of test web host.
This way the I can start the test web host that connects to a clean db running in a container, run the tests.
Does this approach sound right? OR Has someone used dotnet-testcontainers to spin up a container for their application tests and what approach worked.
I wrote about this approach here.
You basically need to create a custom WebApplicationFactory and replace the connection string in your database context with the one pointing to your test container.
Here is an example, that only requires slight adjustments to match the MSSQL docker image.
public class IntegrationTestFactory<TProgram, TDbContext> : WebApplicationFactory<TProgram>, IAsyncLifetime
where TProgram : class where TDbContext : DbContext
{
private readonly TestcontainerDatabase _container;
public IntegrationTestFactory()
{
_container = new TestcontainersBuilder<PostgreSqlTestcontainer>()
.WithDatabase(new PostgreSqlTestcontainerConfiguration
{
Database = "test_db",
Username = "postgres",
Password = "postgres",
})
.WithImage("postgres:11")
.WithCleanUp(true)
.Build();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.RemoveProdAppDbContext<TDbContext>();
services.AddDbContext<TDbContext>(options => { options.UseNpgsql(_container.ConnectionString); });
services.EnsureDbCreated<TDbContext>();
});
}
public async Task InitializeAsync() => await _container.StartAsync();
public new async Task DisposeAsync() => await _container.DisposeAsync();
}
And here are the extension methods to replace and initialize your database context.
public static class ServiceCollectionExtensions
{
public static void RemoveDbContext<T>(this IServiceCollection services) where T : DbContext
{
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<T>));
if (descriptor != null) services.Remove(descriptor);
}
public static void EnsureDbCreated<T>(this IServiceCollection services) where T : DbContext
{
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider;
var context = scopedServices.GetRequiredService<T>();
context.Database.EnsureCreated();
}
}
There are another two ways to leverage Testcontainers for .NET in-process into your ASP.NET application and even a third way out-of-process without any dependencies to the application.
1. Using .NET's configuration providers
A very simple in-process setup passes the database connection string using the environment variable configuration provider to the application. You do not need to mess around with the WebApplicationFactory. All you need to do is set the configuration before creating the WebApplicationFactory instance in your tests.
The example below passes the HTTPS configuration incl. the database connection string of a Microsoft SQL Server instance spun up by Testcontainers to the application.
Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "https://+");
Environment.SetEnvironmentVariable("ASPNETCORE_Kestrel__Certificates__Default__Path", "certificate.crt");
Environment.SetEnvironmentVariable("ASPNETCORE_Kestrel__Certificates__Default__Password", "password");
Environment.SetEnvironmentVariable("ConnectionStrings__DefaultConnection", _mssqlContainer.ConnectionString);
_webApplicationFactory = new WebApplicationFactory<Program>();
_serviceScope = _webApplicationFactory.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
_httpClient = _webApplicationFactory.CreateClient();
This example follows the mentioned approach above.
2. Using .NET's hosted service
A more advanced approach spins up the dependent database and seeds it during the application start. It not just helps writing better integration tests, it integrates well into daily development and significantly improves the development experience and productivity.
Spin up the dependent container by implementing IHostedService:
public sealed class DatabaseContainer : IHostedService
{
private readonly TestcontainerDatabase _container = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new DatabaseContainerConfiguration())
.Build();
public Task StartAsync(CancellationToken cancellationToken)
{
return _container.StartAsync(cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return _container.StopAsync(cancellationToken);
}
public string GetConnectionString()
{
return _container.ConnectionString;
}
}
Add the hosted service to your application builder configuration:
builder.Services.AddSingleton<DatabaseContainer>();
builder.Services.AddHostedService(services => services.GetRequiredService<DatabaseContainer>());
Resolve the hosted service and pass the connection string to your database context:
builder.Services.AddDbContext<MyDbContext>((services, options) =>
{
var databaseContainer = services.GetRequiredService<DatabaseContainer>();
options.UseSqlServer(databaseContainer.GetConnectionString());
});
This example uses .NET's hosted service to leverage Testcontainers into the application start. By overriding the database context's OnModelCreating(ModelBuilder), this approach even takes care of creating the database schema and seeding data via Entity Framework while developing and testing.
3. Running inside a container
In some use cases, it might be necessary or a good approach to run the application out-of-process and inside a container. This increases the level of abstractions and removes the direct dependencies to the application. The services are only available through their public API (e.g. HTTP(S) endpoint).
The configuration follows the same approach as 1. Use environment variables to configure the application running inside a container. Testcontainers builds the necessary container image and takes care of the container lifecycle.
_container = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage(Image)
.WithNetwork(_network)
.WithPortBinding(HttpsPort, true)
.WithEnvironment("ASPNETCORE_URLS", "https://+")
.WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Path", _certificateFilePath)
.WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Password", _certificatePassword)
.WithEnvironment("ConnectionStrings__DefaultConnection", _connectionString)
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(HttpsPort))
.Build();
This example sets up all necessary Docker resources to spin up a throwaway out-of-process test environment.

A named connection string was used, but the name 'DefaultConnection' was not found in the application's configuration

I have a DbContext named FileManagerContext in my DOTNET 6 API:
public class FileManagerContext : DbContext {
public FileManagerContext(DbContextOptions<FileManagerContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
}
It's a pretty simple DbContext with a simple Entity in it. Anyway, I have this appsettings.json too:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=FM;User=SA;Password=1234;"
},
"AllowedHosts": "*"
}
And here is the startup snippet in Program.cs's top level statement:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<FileManagerContext>(
opt => opt.UseSqlServer("name=DefaultConnection"));
I could use migrations in the case. All thing goes good. I can add migrations and I can update database successfully. But when I run the application and try to use DbContext I get this error:
System.InvalidOperationException: A named connection string was used,
but the name 'DefaultConnection' was not found in the application's
configuration. Note that named connection strings are only supported
when using 'IConfiguration' and a service provider, such as in a
typical ASP.NET Core application. See
https://go.microsoft.com/fwlink/?linkid=850912 for more information.
I've also tried to get the connection string like this:
var cs = builder.Configuration.GetConnectionString("DefaultConnection");
But it returns null. Can anybody help me through please?
From the sounds of the error, your configuration might not be getting picked up.
How is the configuration being created?
Do you see AddConfiguration extension method being called on the Webhost? the contents of the method should look something like the following:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, false)
.AddJsonFile($"appsettings.{envLower}.json", true, true)
.AddEnvironmentVariables()
.Build();
The the call to build may or may not exist. We typically manually create the configuration object because we need to construct the Loggers and then use AddConfiguration extension method to add that object to the host.
If you don't see that, please take a look at the documentation from Microsoft for guidance on how to set it up. Configuration Documentation
Alternatively, you can get the connection string via the Configuration Object and pass it to the UseSqlServer method.
services.AddDbContext<FileManagerContext>((provider, options) => {
IConfiguration config = provider.GetRequiredService<IConfiguration>();
string connectionString = config.GetConnectionString("DefaultConnection");
options.UseSqlServer(connectionString);
});
in the alternate method, a service provider is passed to the action. You can use the provider to fetch the IConfiguration object from the DI container.
use builder.Configuration
builder.Services.AddDbContext<FileManagerContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]);
});
or builder.Configuration.GetConnectionString as #Serge mentions
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
Don't use "name=DefaultConnection" since it is treated as connection string name including "name="
I am using this syntax in program.cs and everything is working properly
var builder = WebApplication.CreateBuilder(args);
.......
builder.Services.AddDbContext<FileManagerContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
and since you are using Ms Sql Server , the connection string for sql server is usually like this
"DefaultConnection": "Data Source=xxxx;Initial catalog=xxx;Integrated Security=False;User ID=xxxx;Password=xxxx;"
and my OnConfiguring code looks like this
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
...your code
}
}
Thanks To #Neil I figured it out. The configurations were not loaded into app. But, since I'm in dotnet 6's top level statements, adding configurations seems a little bit different from #Neil's advice. So here is the working solution:
builder.Services.AddControllers();
var currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
var environmentName = builder.Environment.EnvironmentName;
builder.Configuration
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{environmentName}.json", true, true)
.AddEnvironmentVariables();
// blah blah
builder.Services.AddDbContext<FileManagerContext>(
opt => opt.UseSqlServer("name=DefaultConnection"));
I was trying to scaffold the DB changes by this command :
Scaffold-DbContext -Connection name=YourDBContextObjectName -Provider
Microsoft.EntityFrameworkCore.SqlServer -project Destination_NameSpace
-context YourDBContextObjectName -OutputDir Entities -force
But the issue is the same in the code-first approach as well, In my scenario, I had different appsettings for different ENVs.
The appSettings.json was like so (as you can see, there is connectionStrings information in this file:
Also, my appsettings.dev.json was like so :
The problem is being occurred because when I tried to scaffold the DB changes by command, Firstly it tries to build your project, and because the Redis and RabbitMQ connection information is missed in the appsettings.json file, It cannot connect to the dataSources. so It returns the error.
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Object reference not set to an instance of an object.
System.InvalidOperationException: A named connection string was used, but the name 'DBContextName' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider...
Solution :
Set The correct Project as the Startup project
Add missing information about other connections to the appsettings.json
As you see, regardless of the error, It's not always related to the Database connection string.
I had the same problem but the error occurs when the application was published on the server. My string connection is in the user's secret, what I did was the following:
In program.cs
// Add services to the container.
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer("Name=dbconnection");
});
builder.Services.AddDbContext<DataContext>();
And before publish, select the string connection in data base options and make sure it is the correct:
Publish Options
It works for me in dot 6 API
Hello, the highlighted code worked for

Injecting Connection String into DbContext Class using Autofac in a TopShelf Service (.Net Framework)

I'm creating a service that needs to reference legacy DLLs for our ERP system so unfortunately .NET framework is how I need to do this. I tried using a Core Worker Service but my calls to the DLLs won't work.
I like using TopShelf and looked up DI to use with this and Autofac seems to be the way to go and it has TopShelf support. I don't like keeping any settings in our config files so that it's easier to deploy to other environments. We have a whole system that keeps settings tied to the environment you are in (dev, test, prod, etc...). In Core apps I simply inject the connection string in the startup and all is well. In this app I want to inject the connection string when I start the service and be able to spin up a DbContect class at anytime and have it use that connection string.
Since I scaffold my data layer and didn't want to modify the generated DbContext, so I created another partial class (MyContext.Custom.cs) with a constructor that allows me to pass in the connection string
public MyContext(string name) : base(name)
{ }
In my Program.cs I'm adding in an Autofac container to TopShelf
HostFactory.Run(serviceConfig =>
{
serviceConfig.UseAutofacContainer(container);
// etc...
}
The container is being built in a function, I tried following many examples but I can't seem to get my constructor with the parameter to be the one that is used. Here are a few of the ways I've tried. Each method produces no error but it runs my default constructor where it is looking for the named connection string in the config file.
static IContainer BuildContainer()
{
string myConnectionString = AppConfig.Instance.Settings["MyConnectionString"];
var builder = new ContainerBuilder();
//builder.RegisterType<MyContext>()
// .UsingConstructor(typeof(string))
// .WithParameter("name", myConnectionString)
// .InstancePerLifetimeScope();
//builder.Register(c => new MyContext(myConnectionString)).As<MyContext>();
//.WithParameter("name", myConnectionString);
//builder.RegisterType<MyContext>()
// .WithParameter("name", myConnectionString)
// .AsSelf();
//.InstancePerLifetimeScope();
builder.Register(c => new MyContext(myConnectionString)).AsSelf();
builder.RegisterType<MainService>();
return builder.Build();
}
I've tried each variation with .As and .AsSelf(). I've put in the .InstancePerLifetimeScope() and also left it out. I'm not really sure what to set the scope to in this case but figured it should work with lifetime. No matter what I've tried I can't seem to get it to use my constructor.
If I've left out any information feel free to ask and I can fill it in. Hopefully someone has done this. I guess I could pass in the connection string everytime I instantiate a DbContext but I was hoping to get it to work like a Core app which is so much nicer.
thanks
EDIT:
Adding code to show how I'm using my DbContext
public bool Start()
{
using(var db = new MyContext())
{
var warehouse = (from w in db.Warehouses
where w.WarehouseCode == "ABCD"
select w).FirstOrDefault();
}
// other code...
}

Runtime change connection string in Asp.Net Core

I am working on an ASP .NET Core application. i have to change database connection on runtime.
Here i am using appsettings.json file.
Please, I have tries a lot but I nothing seems to be working so I need your help.
For example:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
optionsBuilder.UseMySql(configuration
["ConnectionStrings:Defaultconnection"]);
}
Thanks in advance.
I can think of a couple of ways to do this..
1) Create a DbContextFactory which will create your instances for you. Maybe apply an attribute to each context which will allow you to fetch out the relevant db connection string from your connection string collection at the point that the factory makes the instance? A bit of reflection in the DbContextFactory would do that easy enough.
2) If the collection of DbContexts is not likely to change, and/or the connection strings either - you could register each DbContext manually using the AddDbContext<>() extension method in the ServiceCollection and then just inject the DbContext that you want into the classes you want

Setting the SQL connection string for ASP.NET Core web app in Azure

I have created a new ASP.NET Core web application in Visual Studio 2015. I've also set up an Azure web app to pull in the app from GitHub and run it. This works fine, but I'm having trouble connecting to the database on Azure.
Locally, this works, and it uses config.json and in code Data:DefaultConnection:ConnectionString for the connection string.
How can I leave the code as it is, and have it work in Azure too? I've tried setting the Application Settings in the portal, both the Connection Strings and the App Settings. And using both "SQLCONNSTR_DefaultConnection" and "Data:DefaultConnection:ConnectionString" as the key.
(Setting the App Settings doesn't seem to work by the way. I think the value I provide is too long).
So how can I provide the connection string for my Azure database to my Azure web app (ASP.NET 5), without checking it in in source control?
Update
My Startup.cs looks like this (see the full file on GitHub):
public Startup(IHostingEnvironment env)
{
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
configuration.AddUserSecrets();
}
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
In the ConfigureServices method, there is also:
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
and, also in the ConfigureServices method:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]))
.AddDbContext<InvoicesDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
// So this is where I want my app in Azure to use the connection string I
// provide in the portal
Short answer
I've tried setting the Application Settings in the portal, both the Connection Strings and the App Settings. And using both "SQLCONNSTR_DefaultConnection" and "Data:DefaultConnection:ConnectionString" as the key.
You're close.
Go to Azure web app > configure > connection strings.
Add a connection string with the name DefaultConnection.
Use Configuration.Get("Data:DefaultConnection:ConnectionString") to access it.
Example using timesheet_db instead of DefaultConnection
This is an example from my own timesheet application. My connection string was named timesheet_db. Just replace all instances of that string with DefaultConnection to adapt the example to your use case.
Azure web app configuration
Azure web app service control manager
The online service control manager at https://myWebAppName.scm.azurewebsites.net/Env will show the connection strings.
Startup.cs
Setup configuration settings in Startup so that the environmental variables overwrite the config.json.
public IConfiguration Configuration { get; set; }
public Startup()
{
Configuration = new Configuration()
.AddJsonFile("config.json")
.AddEnvironmentVariables(); <----- will cascade over config.json
}
Configure the database in Startup.
public void ConfigureServices(IServiceCollection services)
{
services
.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ProjectContext>(options =>
{
var connString =
Configuration.Get("Data:timesheet_db:ConnectionString");
options.UseSqlServer(connString);
});
}
Of course, the example uses a connection string named timesheet_db. For you, replace all instances of it with your own connection string named DefaultConnection and everything will work.
In RC2 I had to change how my connection strings were read to get them to work in Azure. In my case I had to ensure the Azure connection string was named "DefaultConnection", and accessed by:
RC1:
{
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=db;Trusted_Connection=True;"
}
}
}
Accessed by:
var conn = Configuration["Data:DefaultConnection:ConnectionString"];
RC2:
{
"Data": {
},
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=db;Trusted_Connection=True;"
}
}
Accessed by:
var conn = Configuration.GetConnectionString("DefaultConnection");
You have a number of options to set your connection string.
The default setup class get the enviroment settings from different sources.
You could set your connection string in config.production.json. or config.staging.json. See the startup class
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This reads the configuration keys from the secret store.
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
configuration.AddUserSecrets();
}
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
I think you are looking for SlotSticky Settings
Use this command in Azure PowerShell to set 2 app settings as sticky to the slot
Set-AzureWebsite -Name mysite -SlotStickyAppSettingNames #("myslot", "myslot2")
And this command to set 2 connection strings as sticky to the slot
Set-AzureWebsite -Name mysite -SlotStickyConnectionStringNames #("myconn", "myconn2")

Categories