Set two different environments in a dll? - c#

I have implemented a dll for consuming an API. It's working properlyn but now I have to allow user to consume the dev and production API's (we have separate environments for dev and production). I donĀ“t know how to do it.
I can implement a switch and allow the user to set production or dev and just change the path, but I'm guessing this is bad practice.
Actually, I have a path stored in app.config. Any suggestions on how to implement this or is the only way to send the user two dll's, one for production and one for dev?

The correct way to do this would be:
to store the path in app.config
create environment specific transforms for the config file
transform the config file during deployments
Sample config:
<add key="apiUrl" value="http://localhost/myapi" />
And corresponding transform file
<add key="apiUrl" value="http://devhost/myapi" xdt:Transform="Replace" xdt:Locator="Match(key)" />
You can use plugins like SlowCheetah to do the transformation.
Alternatively, you can use MSBuild tasks to do the transformation as well:
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="TransformWebConfig">
<TransformXml Source="C:\myapp\app.config"
Transform="C:\myapp\app.dev.config"
Destination="C:\myapp\bin\<web.config or in case of exe, product.exe.config>"
StackTrace="true" />
</Target>
Note: DLL files in .Net cannot have config files. So I'm assuming you're referring to the exe or web.config of your product.

Conditional compilation could be an option for this.
It's not too hard to set up. A good article is:
http://www.codeproject.com/Articles/451734/Visual-Studio-Use-Conditional-Compilation-to-Contr

Related

How to manage connectionstring for CD

I have a legacy .net application running 4.0 that I have been given to maintain and as part of the maintenance I am updating the CI/CD system.
In doing that I found a bunch of manual file content replaces used to manage environment specific variables.
One of these is the connection string, I was wondering what the recommended way of handling connection strings in app and web.config files is across environments.
I tried slowcheetah but it doesn't do the transform for the web.config when you are not using the publish option.
Thanks for any help.
Yes you can do this with a msbuild script:
<UsingTask TaskName="TransformXml" AssemblyFile="bin\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="GenerateConfigs">
<MakeDir Directories="$(BuildOutput)" Condition="!Exists('$(BuildOutput)')"/>
<TransformXml Source="BTSNTSvc.exe.config"
Transform="BTSNTSvc.exe.$(Configuration).config"
Destination="$(BuildOutput)\BTSNTSvc.exe.config"/>
</Target>
Please review the following resources for performing a config transform without having to do a publish. This snippet was taken from the 2nd link.
http://matthewvukomanovic.blogspot.com/2012/10/webconfig-transform-without-publishing.html
http://geekswithblogs.net/EltonStoneman/archive/2010/08/20/using-msbuild-4.0-web.config-transformation-to-generate-any-config-file.aspx

Switch different app.config

I have a windows console application. I get some values from app.config. There are for writing log files. When debug, I want to write them on my desktop. But when publish the application, the logs should be in the remote server. Now I have to manually set the values each time between debugging and publish.
Can we create two version app.config files and use them properly?
When check in, we only check in the app.config for production.
Please remember, it is not an asp.net web application. In that case, we can have different web.config files.
Yes you can, but you need an extension to do it. I have used SlowCheetah for this in the past.
Use SlowCheetah to add a transform for your release build that changes the log-path. Your app.Release.config transform file could look something like this.
<?xml version="1.0" encoding="utf-8" ?>
<!-- For more information on using transformations
see the web.comfig examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="logDirectory" value="D:\releaseLogs" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
You can add a transform for Test and Debug environments as needed.
In your source control you would check in:
An app.config that contains all the keys your application needs
An app.Release.config that contains transforms that changes the keys that need to be different in your Release environment.
When your solution is built, SlowCheetah will apply the correct transforms to your app.config based on the target environment.
Another alternative to extensions is to simply add a Post-Build event in your project:
if "Debug"=="$(ConfigurationName)" goto :nocopy
del "$(TargetPath).config"
copy "$(ProjectDir)\App.Release.config" "$(TargetPath).config"
:nocopy
Copy your App.config to App.Release.config and change as needed, when you publish as Release the appropriate file will be used.

Can we share some contents of App.config between projects?

I have two independent projects in my Visual Studio 2008 solution. Both has its own App.config. But in one project, I need one or two properties defined in another project's App.config. Is it possible to share part of the App.config contents from other project?
Yes - of course. Any configuration section can be "externalized" - e.g.:
<appSettings configSource="AppSettings.DEV.config" />
<connectionStrings configSource="MyConnection.config" />
or
<system.net>
<mailSettings>
<smtp configSource="smtp.TEST.config" />
vs.
<system.net>
<mailSettings>
<smtp configSource="smtp.PROD.config" />
Any configuration section can be put into a separate file that can be shared between projects - but no configuration section groups, and unfortunately, it's sometimes a bit tricky to know which is which.
Also, in some cases, Visual Studio will complain (using red wavy underlines) that the "configSource" supposedly isn't valid - but it is - it's defined on the ConfigurationSection object in the .NET config system.
UPDATE:
another feature that hardly enough developers seem to know and use is the ability in Visual Studio to add existing files from a different project as a link:
With this, you can add links to files into your local project, and they'll always be kept up to date. Great productivity booster if you need to do some file-level sharing (like for common configuration files or such)!
Try this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="PROD.config">
<add key="common.Currency" value="GBP" />
</appSettings>
</configuration>
Only the "running" app.config is used, but you can go external like marc_s says.
You can also create a .Settings file that's "shared". Go to the "shared" project properties, the Settings tab on the left, create a setting with Application scope, and set the Access Modifier on top to Public. In your other project you can then use ClassLibrary1.Properties.Settings.Default.SettingName to access it. It will be strongly typed, but you may need it at compile time.
Something I like to do, especially when trying to coordinate ServiceModel elements between libraries and tests is to use configSource to fragment the config in the target library and simply link/copy always the fragments in my test projects.
That way I only maintain in one location.
You could take it one step farther and simply have a common directory in the solution and link the fragments in all projects.
In that situation, I would think using a Database to store some configuration data would be ideal. Each app does its own thing, but they look to a shared database to get those common pieces of information.
EDIT: I spoke too soon! Looks like both the OP and I learned something about config files =D

Centralised settings in C# for multiple programs

I was recently given a heap of programs to maintain and I am trying to find some help in adopting some best practices. They are essentially three independant softwares which use a common DLL to manage a series of settings the apps all share. The DLL works this way: it swaps the user settings file (XML file buried deep in the user's configurations folder in Windows), with a fix file, specified by a hardcoded (egad!) path.
The rationale behind keeping it as user settings and not app settings is that the DLL can be found in several locations (one for each app that will use it), and so the user settings file is common (if all copies of the DLL are the same compile), whereas by using application settings there would be as many app.config files as there are copies of the DLL.
I'm trying to conceive of a better way to centralise these configurations and end the senseless file swapping. One approach (actually, most likely the best approach) would be to redesign all 3 apps so they all use a central dll with its own "app.config". Are there other more recommendable venues?
Have you considered using the Windows Registry? We all hate it, but maybe it's the best option in this case as it is centralized and you could share settings easily across applications.
EDIT: If you don't like the Registry (and I don't blame you for it), you can create an XML or some other configuration file in a directory under the Application Data special folder. This is how this is done these days as far as I know.
string appData = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData));
string folder = "MyApplicationName";
string fileName = "settings.xml";
string path = Path.Combine(Path.Combine(appData, folder), fileName);
For this precise problem, if it's a .Net DLL, you could use the GAC, this would centralise your DLL. All software would know where they could access this DLL. And in this way, you could have less refactoring to do.
This is only a patch for this problem only. I would not recommend this, for new developpement.
GAC in Wikipedia
You can use settings in a common file - most likely stored under AppData in the users settings folder
The advantage here is that you do not have to modify any code whatsoever.
The application would store its settings in its normal config file and refer to the common file for the dll settings:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="commondll.config">
<add key="KeyToOverride" value="Original" />
<add key="KeyToNotOverride" value="Standard" />
</appSettings>
</configuration>
Then in the common file commondll.config:
<appSettings>
<add key="KeyToOverride" value="Overridden" />
<add key="KeyToBeAdded" value="EntirelyNew" />
</appSettings>
Perhaps this solution that shares configuration settings between an ASP.NET app and Console app will contain useful information.

Multiple .NET Configuration Files and Setup Project Problem

In order to handle settings for different deployment targets, I moved application settings from app.config to its own file and included that file in app.config via configSource. I also created a settings file for each target.Here is an illustration:
Project A
app.config (references settings.config)
settings.config
settings.Release.config
settings.Debug.config
During post-build, I copy the appropriate settings.{configuration}.config to the output directory. This is working fine so far and I can see settings.config file in the project output directory containing settings for the current build configuration: Release, Debug, etc.
However, I am having a problem with the setup project that I have for this project (Project A). Initially, it was not including settings.config file. So I set the build action for settings.config file as Content and I added content files from Project A to the setup project. This ensured that settings.config file was included in the setup. However, since the setup project appears to be picking settings.config file from the project directory instead of the output directory, settings.config file included in the setup is not what it should be. I want the one from the output directory to be included in the setup program since that one is the correct one for the current build configuration. I tried the following:
Added settings.config as a file to the setup project. However, it seems like I can only specify absolute path. So when I add it from the output directory of a particular build configuration (..bin\debug\settings.config), it does not work in other build configuration since (..bin\debug\settings.config) does exist in the directory specified. I looked into using relative paths or dynamic paths in the setup project where the build configuration could be specifed as part of the path but I could not find anything.
I considered using pre-build event to actually modify settings.config file in the project directory and then have it copied over the output directory by setting its 'Copy to Output Directory' to copy always or copy if newer. This should ensure that the appropriate settings.config is copied to the output directory just like the post-build based solution and should also ensure that the contents of settings.config file is updated before the setup project includes it. However, I don't like this solution because I would have to make sure settings.config file is writeable before I can make any changes since it is source controlled. If it is readonly, then I need to flip it to writeable, make changes, and then set it to readonly again. It is adding extra complexity.
I was wondering if anyone has a better idea or knows a setup project trick that allows me to include settings.config file appropriate for the current build configuration in the setup program.
Thanks
If I had to approach this problem, I'd start by asking the following question:
Why does settings.config have to be under source code control if settings.Debug.config or settings.Release.config provide the same information?
The answer, if I read your question correctly, is because you needed to force a settings.config file to appear as part of the build output. I'm guessing this is because your setup project is using the built in "Primary output" choice.
What you can do instead is add that file to your setup project as an explicit file reference. Right-click on the setup project and choose add / file, then select the file you want to include. As you'll notice (unless it's been fixed in VS2008 which sadly I'm not yet allowed to use at work), there is a very annoying limitation placed on manually added files - there is no way to make the path build configuration aware. You can work around that by copying the appropriate settings.config file to a common location (e.g. bin/Configuration) and picking it up from there. This does limit you to building Debug and Release versions sequentially, rather than in parallel, but for many this probably isn't a huge problem.
If you aren't required to use VS setup projects, I strongly encourage you to take a look at WiX (Windows Installer XML - see http://wix.sourceforge.net/ for more information). That will easily allow you to accomplish what is necessary, although if you are unfamiliar with the internal workings of Microsoft Installer the initial learning curve could be a little steep. Microsoft use WiX themselves for some pretty significant setup tasks (e.g. Office 2007, SQL Server, etc.). It had been hoped that WiX would become part of Visual Studio (for VS 2010), but sadly that is no longer the case.
I decided to go about achieving the same result (being able to have different configuration settings for different target environments) in a different way. So here is how I implemented it and it is working great. I read some of the posts here at SO about XmlMassUpdate task from MSBuild Community Tasks and decided to utilize it. Here is what I did:
1) For each project that needs to have different settings depending on the target environment, I added an xml file called app.config.substitutions.xml or web.config.substitutions.xml to the project. So, the project looked like
Project A
app.config
app.config.substitutions.xml
app.config.substitutions.xml file has the settings substitutions that XmlMassUpdate will process and apply to app.config file. Below is a sample substitution file that I use:
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
<substitutions>
<Development>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="DevValue" />
</appSettings>
</Development>
<Test>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="TestValue" />
</appSettings>
</Test>
<Release>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="ReleaseValue" />
</appSettings>
</Release>
</substitutions>
</configuration>
For details on how to specify substitutions, take a look at the documentation for XmlMassUpdate or just do a search on it.
2) Now I need to run XmlMassUpdate as part of build automation (TeamBuild/MSBuild). So in BeforeCompile in TeamBuild build definition file (basically a proj file), I added the following to run XmlMassUpdate on config files that have a corresponding .substitution.xml file
<PropertyGroup>
<SubstitutionFileExtension>.substitutions.xml</SubstitutionFileExtension>
<TargetEnvironment>Test</TargetEnvironment>
</PropertyGroup>
<Target Name="BeforeCompile" Condition="'$(IsDesktopBuild)'!='true'">
<CreateItem Include="$(SolutionRoot)\**\app.config;$(SolutionRoot)\**\web.config">
<Output ItemName="ConfigurationFiles" TaskParameter="Include"/>
</CreateItem>
<CreateItem Include="#(ConfigurationFiles)" Condition="Exists('%(FullPath)$(SubstitutionFileExtension)')">
<Output ItemName="ConfigFilesWithSubstitutions" TaskParameter="Include"/>
</CreateItem>
<Message Text="Updating configuration files with deployment target specific settings..."/>
<XmlMassUpdate
ContentFile="%(ConfigFilesWithSubstitutions.FullPath)"
SubstitutionsFile="%(ConfigFilesWithSubstitutions.FullPath)$(SubstitutionFileExtension)"
ContentRoot="/configuration"
SubstitutionsRoot="/configuration/substitutions/$(TargetEnvironment)"/>
</Target>
Note that config files are read-only during the build, I make sure to set them writeable before running this task. I actually have another custom MSBuild task that runs before XmlMassUpdate that handles common settings throughout all of the config files such as connection strings. That task makes the config files writeable. I also don't check modified config files back to the source control. They're (appropriate config file for the deployment target) included in the installer.

Categories