Using external config file for connection strings in multiple projects - c#

I want to know if there is a better way to use connection strings in external files in my solution, since my solution has 8 separate projects, all using the same connection strings. According to this:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings-and-configuration-files#using-external-configuration-files
using an external file (for connection strings) is trivial, and can be easily done (using something like configSource="connections.config). However, in order to make this work, We have to follow these guidelines:
The external file has to exist within the project folder
The external file has to have the property "CopyToOutputFolder" set to true
In my case, this can be done, but it's problematic to manage this throughout all 8 projects, especially if I need to add a new connection string, change one of the settings (target database, username, password), or when I need to add a new project.
--FYI: each of these projects are web jobs that are deployed to an Azure server. Adding a new web job for a new feature, new utility, etc., or removing an old web job for any number of reasons is not uncommon. This solution is constantly changing, which adds to the difficulty of managing these settings within each project separately. Also, connection string management on the production server is not problematic, since each web job inherits these connection strings from the parent app service.
I would like to know if any of the following is possible;
Can I use a separate file that is NOT within the project folder for the connection strings?
Can I load connection strings dynamically (at run-time) into the configuration manager? (one note on this - I know this is possible, but I need to be able to do it without affecting the underlying app.config file)
Is there another type of ConfigurationManager that is commonly used to load these connection strings into the solution, that will meet my requirements?

Although this question is over a year old, I thought an up-to-date answer would be useful for any Windows desktop coders wanting to reference connection strings outside the project folder's app.config etc. This would be useful for sharing 1 config file for multiple projects, having to only change connection string in 1 centralised file.
The basics of telling App.Config to use a different file for connection strings, is straightforward; however, .NET appears NOT to be able to parse this external file, if it is either NOT in the project's root folder, or a subfolder(s) within that project. Let's see this by looking at the most basic example:
in the project's App.config, within the <configuration> tag, use the code below ('configSource' points to another file to read the connection strings):
<configuration>
<connectionStrings configSource="ExternalConnectionStrings.config"/>
</configuration>
Create ExternalConnectionStrings.config, with the following code:
<connectionStrings>
<clear/>
<add name = "ConnString1"
connectionString = "Server=myServer;Trusted_Connection=True;Database=myDB;Persist Security Info=false"/>
<add name = "ConnString2"
connectionString = "Server=tcp:azureserver.database.windows.net,1433;Database=AzureDB;User ID=**;Password=**;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Persist Security Info=false"/>
</connectionStrings>
Save this config file in the same folder as your project's App.config and mark this file's property to 'Copy to Output Directory' or 'Copy if newer'.
This is the 'textbook' way of referring to connection strings from another file other than App.config. However, this may not be that useful: how to have this external file in a completely different folder from the project folder?
To do this, copy that same ExternalConnectionStrings.config to a folder OUTSIDE the project or solution folder, eg. C:\ConnectionStringsFolder\. REMOVE this config file from the project folder where we previously copied/created it (otherwise the following instructions will not work).
Keep App.config the same (making sure the ExternalConnectionStrings.config is not present in the project folder). Use the Windows mklink command to associate ExternalConnectionStrings.config to an external folder, with the following command prompt command:
mklink ExternalConnectionStrings.config C:\ConnectionStringsFolder\ExternalConnectionStrings.config
Windows should return with a 'symbolic link created for....'; make sure you did NOT have that particular config file present in the project folder where app.config sits.
You should see the ExternalConnectionStrings.config listed within eg. Visual Studio; make sure you mark this to COPY to the output folder (I use 'Copy if newer', which will pick up any changes to the external config file ONLY after a rebuild of the project).
This answers Matt Spinks' question 1; for question 2, the following method will return all connection strings found within the external file (as pointed to by App.config):
using System.Diagnostics;
using System.Configuration;
static void GetConnectionStrings()
{
ConnectionStringSettingsCollection cStringsSettings = ConfigurationManager.ConnectionStrings;
if (cStringsSettings != null)
{
foreach (ConnectionStringSettings cstrings in cStringsSettings)
{
Debug.WriteLine(cstrings.Name);
Debug.WriteLine(cstrings.ConnectionString);
}
}
}
This is currently working in Visual Studio 2019, with .NET Core 3.1, a WPF application, on Windows 10.

You don't mention which version of dotnet you are using, however if you are using dotnet core the application configuration can be sourced from environment variables.
I think this could solve your problem because you don't need to track any additional files between your projects and in my opinion makes a lot of sense because any secure data would not be visible to any developer or be stored in any repository. Only devops would know about it when they set it initially. Also this gets your closer to the 12 factor app https://12factor.net/.
To use this make sure that your Startup.cs file includes a section to load the environment variables such as
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables()
.Build();
dotnet core Environment Variable cnfiguration provider
You can probably create a bat file to set everything initially
setx connection_string="Server=xxxxxxxxx" /M
This will create your environment variables and set the values you specity.
Hope you find this useful.

Related

Why wont my application read my DataAccessLayer.dll.config file?

I'm trying to use application settings with a C#.NET project I am working on. But I can't seem to get it to return anything other then the default values. Through the project properties I've added a single setting, DBConnectionString, and set its value to a connection string I want to use. Its scope is set to "application".
Doing this created a number of files including Settings.settings, Settings.Designer.CS, and app.config. The Settings class then has custom, type safe, properties that can be used to set and retrieve each setting. The app.config file is a XML file that stores the actual settings values.
When I build my project it looks like the app.config file is copied to the target directory as DataAccessLayer.dll.config. My program runs fine and is able to use the default connection string.
Next I tried to edit the DataAccessLayer.dll.config file to change the connection string. I ran my program again, but it continued to use the default value.
This project is a class library that use to a Web project and sometimes the connection string can changes.
Builds will output config files named after the dll however those aren't actually what's read on app start up. You could put the setting in the web apps config (example here Equivalent to 'app.config' for a library (DLL)), those are the settings you'll actually be running with in this case.
If you want your library to be portable you'll have to either; 1) make your own config class/file 2) Read your dll's app config manually (example in the answer I linked to above) or 3) Put your setting in the importing projects app.config
In this case I would just put your connection string data in the web apps config. If DataAccessLayer.dll is only for internal use, this is in my experience the most common pattern, and doesn't really have many cons. Ultimately I would have these values set during my build or by a deploy utility like Salt or Chef. If you're manually editing the web apps config on or after deploy then you're doing it wrong.

Forcing Entity Framework and ASP.MVC to use the connection string from app.config of another assembly

Set & settings:
I use Entity Framework 5 and have a dll project with edmx file. In this project I have App.config with connection string for the EF model. I have also second project, ASP.MVC 4 web application which is a startup project. It references the database project. Important thing is - db is Oracle and EF uses Oracle custom providers.
Problem:
If I place my connection string in the ASP.MVC startup project is works fine. It's common advice to do this. But I don't want to. I don't see reason why I should. How can I force MVC/EF to find the connection string in App.config of the external library (which as a matter of fact is a data access layer)?
App.Config is used by WinForms, WPF and executable applications.
Web.Config is used in IIS (and is able to set IIS environment specific configurations)
It seems no App.Config will ever be merged to the Web.Config (source):
In using an App.config, file the configuration system merges the
App.config file with content of the Machine.config file when the
application starts and the configuration is applied. This mechanism
allows machine-wide settings to be defined in the Machine.config file.
The App.config file can be used to override the settings of the
Machine.config file; you can also lock in the settings in
Machine.config file so that they get used. In the Web.config case, the
configuration system merges the Web.config files in all directories
leading up to the application directory into the configuration that
gets applied. For more information about configuration and the setting
priorities, see topics in the System.Configuration namespace.
Perhaps you'll find a solution more appropriate to your needs by using Application Configuration Files.
Finally, after facing multiple issues, I've decided to move connection string of DAL into the Web.config of the web application. I was convinced by some arguments you can read here in the post of Chris Ammerman.

Common app.config for multiple applications

I have several C# console applications, which need to have the same set of settings. I want to avoid duplicity and avoid separate app.config for each application.
Is there any way to read a common app.config file (say common.config) for applications (app1.exe, app2.exe).
Create one file called app.config. Put it in some place outside of your projects' directories, like up in the solution directory. Add it to your projects as a linked item with a relative path to the file. Set the right build action for this item (application configuration) in each project.
Now when each project builds, the file will be copied to the project's output dir with the right name.
You can load an external app.config using the code below:
config = ConfigurationManager.OpenExeConfiguration(Path.Combine("C:\test\root", "Master.exe"));
string logpath = config.AppSettings.Settings["Log.Path"].Value;
And save settings as so:
config = ConfigurationManager.OpenExeConfiguration(Path.Combine("C:\test\root", "Master.exe"));
config.AppSettings.Settings["Log.Path"].Value = "C:\newpath";
config.Save();
You might have to have a master config within one of the applications and point the rest to this. Typically this method is considered bad practice though. There might be issues with different applications locking the file.
#Ran's answer is an option, but each application will still have its own config file after you build. At compile time they will be the same, but at deploy time they are copies.
You can also open one application's config file from another application using:
ConfigurationManager.OpenExeConfiguration(string)
You can have an external config file that all applications reference using:
ConfigurationManager.OpenMappedExeConfiguration
And there's the option to using the Machine config file using:
ConfigurationManager.OpenMachineConfiguration()

Configuration files per environment

I'm working on a project with several environments (Local,Development,Main,Prod,Live) that have several config files (Web, ConnectionStrings Windsor, Smtp, Appsettings, Nlog, etc).
The current strategy used is to have one of these config for each branch and to maintain the configs by hand and not to merge any changes.
What are the more elegant options for storing and deploying config files in this sort of set up?
Generally I tried to keep most of these settings in the appSettings (even though I know that's not the "official" right place for them). Then I break the appSettings out into an environment.config file, and have the appSetting element in the web.config reference that through the configSource attribute.
When I setup a new environment, I manually setup the environment.config file for that environment. When, when I deploy a new release, I exclude the environment.config file from teh deployment so that the environment-specific version stays in place.
This lets you deploy new copies of the web.config to include project-related changes, while keeping all of your truly environment-specific settings in a seperate place.
An elegant solution is to use WebDeploy (from Microsoft) for the deployment of your web app. When you run the deployment, you can specify a site specific parameter file which will be used the replace several values in the web.config file.
For deployment, WebDeploy uses a deployment packge, which Visual Studio or msbuild can create.
That way, we have a single deployment package that we can first deploy on a test system, run through several tests and then, when it passes the test, deploy without changes on several servers. Each server has its local parameter file that hardly ever changes and contains the site specific values.
store them on a file server that has the same name in all environments. I am not familiar with all of the configs, but most have ways of doing this. The only problem with this approach is usually dev/local which often share a file server so you have to change the local by hand.
If you actually can't point the configs to a file server, a less elegant solution would be to pull them down via a bat file at deployment or start up. seems like there are alot of options rather than maintaining by hand, that is error prone.

Can a Console Application reference its .exe.config if the .config is in another folder?

Here is the task I have been given at work. We have a Web Application for which I created a Console Application that can be executed by the Scheduled Tasks on a daily basis. The task I have be presented with is to discover if we can place the ConApp.exe and the ConApp.exe.config in two different directories (folders) in our application. We would like to place the .exe file in the bin folder with all the .dll's and place the .exe.config file in a central configurations folder. I have been looking around in the properties and such with in Visual Studio and I do not see any options that will allow me to specify to the ConApp.exe the location of the ConApp.exe.config.
Is there a way to place these two files in separate folders or do they need to be in the same folder and have the .exe.confing reference a central .config file?
Thanks, :)
You should be able to use ConfigurationManager.OpenExeConfiguration to do that. You can add a setting in the console application's config file that points out the path and filename to the config file, and pass that value to OpenExeConfiguration (granted that the console runs as an account that has read access to the location where the config file is stored).
Note that if your console app contains statements like ConfigurationManager.AppSettings["somekey"], these will need some rewriting so that they use the Configuration object returned by the OpenExeConfiguration method.
The automatic discovery of the .exe.config works only if the files are in the same folder. But you gave the answer yourself IMHO: have the .exe.config reference another .config file in the desired central location.
No, you cannot tell your app to look in another directory for its main app.config.
What you can do is externalize certain configuration sections to external files in another directory:
<configuration>
<appSettings configSource="config\appSettings.config" />
<connectionStrings configSource="config\connections.config" />
</configuration>
This works - even though in the Visual Studio designer there will be complaints about this - on any .NET configuration section (but not on section groups, e.g. you cannot externalize the entire <system.web> or <system.serviceModel> at once - you need to do it by their sub-elements.
So with help from Fredrik Mork I was able to figure out this solution. First of all when you create your Console Applications, DO NOT, create any setting in the projects properties window. This will create an app.config file in your project which I believe the executable will try to look for and crash if it doesn't find it. When you first create the Console Application and then Build it before writing any code. Visual Studio create the Debug folder with just the executable file and a few supporting files. I then placed this code in the "main" function:
Dim fileMap As ExeConfigurationFileMap = New ExeConfigurationFileMap()
fileMap.ExeConfigFilename = "....../AppName.config"
Dim externalConfig As Configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None)
Dim appS As AppSettingsSection = externalConfig.Sections("appSettings")
Dim reportURL As String = appS.Settings("URL").Value
Console.Writeline(reportURL)
In the 2nd line "fileMap.ExeConfigFilename = "....../AppName.exe.config" then "......" represent the full pathname to the config file and AppName is the name of your file. Now I also tried to copy the code from my original Console Application and run it but it still crashed. I think it is due to the .dlls that I am using which make calls to Stored Procedures on the database. I believe these dlls are looking for the .config file to be in the same folder since that is the way they were built. However, if you are careful when you begin writing your application you can utilize Web.config information like I did above.

Categories