Why wont my application read my DataAccessLayer.dll.config file? - c#

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.

Related

Using external config file for connection strings in multiple projects

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.

Deploy on premise web app with transformed release configs

We are in the process of setting up our release definitions to finally finish the last piece of our CD/CI setup in VSTS and are running into an issue where we are unable to transform our configs.
Currently we are following Scott Hanselman's approach of keeping connection strings and secrets out of our web.config via a secrets.config because we don't want any of that information committed to source control. If there is a better practice for this scenario we are open to switching but currently unsure of how this would integrate with VSTS CD/CI.
We've tried using the XDT Transform Extension to transform our web.base.config to web.config to remove the file and the configsource references but it doesn't seem to be transforming.
We've also used the IIS Web App Deploy task with the XML transformation and XML variable substitution but the transformation isn't working because the web.base.config doesn't exist in the artifacts. I'm not sure why that is not being pulled in on build.
We'd like to know what best practice is for any or all of these different steps and how we can accomplish an on premise deployment successfully without committing config settings to source control. We'd also like to know how to execute this methodology for console apps as well.
Since the web.base.config file isn’t uploaded to source control and no in build agent machine, so you can’t use XDT Transform or XML transformation task/feature.
You can store the data in secret variable or Azure Key Vault and link it to release definition.
For example:
Add a new variable (click lock icon) or link Azure key Vault in release definition (e.g. connectionStrings)
Way 1: Check XML variable substitution option (Variables defined in the Build or Release Definition will be matched against the 'key' or 'name' entries in the appSettings, applicationSettings, and connectionStrings sections of any config file and parameters.xml. Variable Substitution is run after config transforms.), then the variable value will be replaced to the config files
Way 2: If the web app published as the web deployment package: override web deploy parameters (SetParameters.xml )by specifying value in Override Parameters input in release task. (You can add parameters.xml file to the project, then the related parameters will be added to SetParameters.xml file when generate web deployment package) Configuring Parameters for Web Package Deployment
Way 3: Using Replace Token task to replace the token in a file (e.g. #{mypassword}#) to the variable value (mypassword)

Config file for publishing web service

I have a Visual Studio web service application with the following solution structure (using VS2013 Community):
- [Solution] S
- [Project] S_Service
- S.amsx
- [Project] S_Lib
- File1.cs
- File2.cs
- app.config
The S_Service project is a simple web service project, with just a single asmx file with one WebService method. The project contains a reference to the S_Lib project, a class library to do all the work in terms of the business logic (the request processing).
In S_Lib I have an app.config file in which I store things like directories and file names for stuff which is used by the various components in S_Lib. When I am developing, changes to that file are picked up by the code ok.
Here's the problem: When I publish the S_Service project, the publish directory doesn't contain my app.config - only S_Service.dll and S_Lib.dll. After reading some other posts on StackOverflow (can't seem to find them now), I tried setting the build action on app.config to Content and to Copy Always. Great, this gets the file across to the publish directory, so it looks ok. But, once I deploy the whole lot onto IIS, any changes to the app.config file do not get reflected when the service is run. In fact I can delete the file completely from the IIS directory and it runs just fine. It's as though S_Lib.dll contains a compiled version of the configuration settings. This is no use, as I want to modify the config depending on the machine it's deployed on.
What do I need to do so that app.config is actually used at runtime and that changes are read on the fly?
Just as you wrote, S_Lib.dll contains compiles settings from the time when you set them in VS settings designer. Therefore it is still working (more or less).
You have a web service so you need a web.config. Add one to S_Service project. Then merge app.config content to web.config. Every time you change some setting in S_Lib project you will have to merge changes to web.config as well.
Or you could add app.config to S_Service project as a link by name web.config (not sure if it is possible to create a link with different name). Then when you change settings in S_Lib project they will be referenced in S_Service project automatically.
After failing to find a simple Visual Studio-based solution to do what I want, I implemented a more customised solution. In the library project, I replaced the config lookup method:
internal static string GetConfig(string key) {
return ConfigurationManager.AppSettings[key] as string;
}
with a new method that reads my own settings file (custom format), stored in the solution. It's not perfectly ideal as it means that each project in the solution has to have its own settings file, but it's simpler overall. If anyone is interested please leave a comment and I will elaborate on this solution.

Using one app.config file for multiple projects

I have a solution with the following setup:
X amount of class library projects
Y amount of console application projects
Each of these projects may have 0 or more configuration parameters.
Now, I'd like to have only one App.config for user to specify settings and that App.config will only contain parameters of all the reference projects of the console application project to be run.
I've tried giving each project a Settings file and then linking them to the console applications according to their dependencies but that didn't work.
I've also tried just lumping all the configurations together in one class library project and have each console application link to that app.config (or settings file). But that also didn't work (i.e. changes of the app.config or the settings file in the class library will not update the .config of the executable)
Is what I am trying to do possible?
Yes, it is possible. You just need to open the app file. Follow the next example:
ConfigurationManager.OpenExeConfiguration("C:\Test\SomeProject.dll");
XmlNode loggingConfigNode = ConfigurationManager.GetSection("log4net") as XmlNode;
I guess that you will have to open each setting file in order to use the settings, or you will have to consolidate all the settings in a single app.config and then your applications can access the file by open it.

.NET Modifying Class Library Application Settings from Host Application

Let's say I have the following scenario in .NET 4.0:
- Solution containing: a) a Class Library b) a Console Application
The Console Application references the Class Library.
What I want is to setup some Application Settings in my Class Library and make it so that these are accessible by the Class Library (host Console Application should not make use of these directly), but also modifiable via a .config file after deployment (so if the user decides they want to change the value of one of the settings, they can do so without having to re-deploy the application.
Is this possible?
As far as I know, the class library will have access to the host files config file via dot net configuration management framework. You can also have a second configuration file with a name known to your library and load it manually at library initialization using the swme framework.
Please Reference System.Configuration
var configurationFileMap = new ConfigurationFileMap(#"c:\myconfig.config");
var configuration = ConfigurationManager.OpenMappedMachineConfiguration(configurationFileMap);
Both the host and the lib should be able to access the myfile.config as long as they know the name of it. If you want to make it more general, you can store the name of the config file under a known registry key
Figured this out after quite a bit of Google-fu.
The final solution I adopted was the following:
I created Settings (which generated an app.config file) in my Service application. I then created links to these settings in both my Code Library project and Console Application project (this is done by going to Add > Existing Item > then hitting the drop-down arrow next to the Add button and selecting "Add as Link". Although the one in the Code Library project is not necessary.
What this does is make it so that I only have 1 settings configuration while in development, while it will still generate a config file for my console application which I can access from both the console application and the Code Library during the development process.
Finally, I used the code below to open the configuration file and access the values. As a disclaimer, there may be an easier way of doing this, but I tried about a 100 combinations and nothing else worked:
String cfgFileName = "IntercompanyConsoleApp.exe.config";
ExeConfigurationFileMap cfgMap = new ExeConfigurationFileMap();
cfgMap.ExeConfigFilename = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + cfgFileName;
Configuration cfg = ConfigurationManager.OpenMappedExeConfiguration(cfgMap, ConfigurationUserLevel.None);
// IntercompanyService is the name of my service app, which is where the real app.config file resides -- hence the entries in the xml are based on this application.
// Also, the scope of my settings entries is Application
ClientSettingsSection section = (ClientSettingsSection)cfg.GetSection("applicationSettings/IntercompanyService.Properties.Settings");
Console.WriteLine(section.Settings.Get("Server").Value.ValueXml.InnerText);
Console.WriteLine(section.Settings.Get("Database").Value.ValueXml.InnerText);
This is an obscure issue, but hopefully this saves someone some time in the future because I spent about 4 hours trying to figure this out.

Categories