getting connection string name in azure worker role - c#

I am currently injection the concrete ConfigFileSettings of the IConfigFileSettings into classes that require a name for the connection string. The simplified code looks as follows:
public interface IConfigFileSettings
{
string GetConnectionString(string name);
}
public class ConfigFileSettings : IConfigFileSettings
{
public string GetConnectionString(string name)
{
return ConfigurationManager.ConnectionStrings[name].Name;
}
}
This works fine for webapi's hosted in iis, windows services and console application. I guess:
ConfigurationManager.ConnectionStrings[name].Name
won't work in worker roles. Can I adapt the GetConnectionString method to make it work in all environments transparently? Also even if I get the connection sting name (e.g. from a .cscfg file) my code will look for:
<connectionStrings> ... </connectionStrings>
I guess I cannot just add an entry into the .cscfg file?

You can put them in the .cscfg file like:
<?xml version="1.0"?>
<ServiceConfiguration serviceName="Web.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="3" osVersion="*" schemaVersion="2013-03.2.0">
<Role name="Worker">
<Instances count="2" />
<ConfigurationSettings>
<Setting name="connectionstringname" value="connectionstringvalue" />
</ConfigurationSettings>
</Role>
</ServiceConfiguration>
Then you can read them by:
var connectionstring = RoleEnvironment.GetConfigurationSettingValue("connectionstringname");

This is a small utility for strong typed config in azure implemented with Castle Windsor DictionaryAdapter. I use it in my projects. Usage :
Explore the code a bit to get the idea
Define your strongly typed config interfaces - Look at Configuration.Interfaces assembly
Define your factories / use azure config provider in composition root and use dictionary adapter to fill the config dictionaries -
Inject your config like in MyWorkerService.cs
using Configuration.Interfaces;
using Persistence.Interfaces;
using Worker.Services.Interfaces;
namespace Worker.Services
{
public class MyWorkerService : IMyWorkerService
{
private readonly IConnectionStrings _connectionStrings;
private readonly IAzureServiceConfiguration _azureServiceConfiguration;
private readonly IMicrosoftStorageConfig _microsoftStorageConfig;
private readonly IPersitenceServiceConfigDependent _persitenceServiceConfigDependent;
private readonly IAppConfigSettings _appConfigSettings;
public MyWorkerService(
IPersitenceServiceConfigDependent persitenceServiceConfigDependent,
IConnectionStrings connectionStrings,
IAzureServiceConfiguration azureServiceConfiguration,
IMicrosoftStorageConfig microsoftStorageConfig,
IAppConfigSettings appConfigSettings)
{
_connectionStrings = connectionStrings;
_azureServiceConfiguration = azureServiceConfiguration;
_microsoftStorageConfig = microsoftStorageConfig;
_persitenceServiceConfigDependent = persitenceServiceConfigDependent;
_appConfigSettings = appConfigSettings;
}
public string DoWork()
{
_persitenceServiceConfigDependent.ConfigDependentAction("blah");
var configSetting = _microsoftStorageConfig.StorageConnectionString;
return $"Job done :" +
$" <br> msConfig : {configSetting}, " +
$" <br> azureConfig.ServiceBusConnectionString:{_azureServiceConfiguration.ServiceBusConnectionString} " +
$" <br> webConfig.SubscriptionId:{_appConfigSettings.SubscriptionId} " +
$" <br> connectionStrings.DefaultConnection :{_connectionStrings.DefaultConnection}";
}
}
}

Related

Creating a dictionary with Unity Application Block IoC

I would like to create a dictionary and populate it in the configuration file for Unity like shown here. However, that example seems to be from an older version of Unity IoC but I would like to know how to do this with Unity 4.
<type ... to="MyTypeServer">
<typeConfig>
<property Name="Converters" KeyType"string" ValueType="IConverter">
<entry>
<key>csv</key>
<value><dependency name="csvConverter"/></value>
</entry>
<entry>
<key>xml</key>
<value><dependency name="xmlConverter"/></value>
</entry>
</property>
</typeConfig>
</type>
<type name="csvConverter" from="IConverter" to="MyCsvConverter">
</type>
<type name="xmlConverter" from="IConverter" to="MyXmlConverter">
</type>
And here is the class:
public class MyTypeServer
{
public IDictionary<string, IConverter> Converters
{
set;
private get;
}
public void DoConversion(string fileName)
{
string fileType = Path.GetFileExtension(fileName);
IConverter converter = Converters[fileType];
if (converter != null)
converter.DoConversion(fileName);
..
...
}
}
I have been trying for hours and researching but no luck.
From the codeplex link you posted:
This is a quick pseudo code of the type of stuff we can have with dictionary.
To me, this reads "we could do something like this if we implemented the feature". Aligns with my experience with unity, I've never come across something like this.
What you can do however: register all the converters, have them all injected as array and then build the dictionary yourself.
// register in code or in xml...
container.RegisterType<IConverter, XmlConverter>( "xml-Converter" );
container.RegisterType<IConverter, JsonConverter>( "json-Converter" );
internal class ConverterConsumer
{
public ConverterConsumer( IConverter[] converters )
{
_converters = converters.ToDictionary( x => x.FileType, x => x );
}
#region private
private Dictionary<string, IConverter> _converters;
#endregion
}
public interface IConverter
{
string FileType { get; }
void DoConversion( string fileName );
}
Posting as an answer because I do not have sufficient points to comment. This is what I did to solve my problem. Not exactly a Dictionary approach but it might help.
My requirements - Store app settings in the Unity XML file as opposed to app.config
I had various objects which were registered in the Unity XML and they had properties like connection strings, Azure queue names , Azure blob container names, etc. I found myself duplicating these values in the XML very often. I could make my objects read values from *appSettings** element of app.config or some other configuration section. However, I chose not to use the app.config for the sake of keeping my objects more testable.
My solution - Use the <instance> element to register reusable string values
I registered all reusable connection strings in a single location as shown below and without any duplications:
<instance name="cnstring1" value="blah connection string 1"></instance>
<instance name="cnstring2" value="blah connection string 2"></instance>
<instance name="azurequeue1" value="name of receiver queue "></instance>
<instance name="azurequeue2" value="name of sender queue "></instance>
<instance name="azurestoragecnstring" value="your azure storage account connection string 0001"></instance>
Reference the name-value pairs using the dependency element whereever required.
Example XML:
<register name="i2" mapTo="someimplementation" type="someinterface">
<property name="Database">
<dependency name="cnstring1" />
</property>
</register>
Example C# code snippet:
public string Database { get; set; }
At run time the property Database of the object with the registration i2 will be set to the value of blah connection string 1
Caveats
Storing connection strings in plain text XML might not be safe especially if the connection string has username and password values.

How can I retrieve a generated value from my C#-code into my web.config file?

I know, that the following code looks naive, but it should only bring to mind, what I want to achieve.
My web.config file:
<connectionStrings>
<add name="ADConnectionString" connectionString="AppData.GetConnectionString()" />
</connectionStrings>
I want to get the string from C#:
public class AppData
{
public static string GetConnectionString()
{
return "LDAP://expample.domain.com:389/DC=example,DC=domain,DC=com";
}
}
I know it is possible to get data from the web.config in the C# code (The AppSettings for example). But is the opposite also possible?
I dont think it is possible as it is just an XML file. All you can do in XML file is add comment,create nodes,add attributes or add nested elements etc etc but you cant add any code to it.
However I think there are certain languages that allow you to do so
https://stackoverflow.com/questions/24486772/write-php-code-inside-xml-file
I DO NOT recommend use it. But you actually can do something like this:
using System.Web.Configuration;
using System.Configuration;
var config = WebConfigurationManager.OpenWebConfiguration(null);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["ADConnectionString"].ConnectionString = AppData.GetConnectionString();
config.Save();
Use Reflection
Here's an example:
private string test = WebConfigurationManager.ConnectionStrings["Test"].ConnectionString; //returns "ExecuteTest" -- note! no parenthesis!
protected void Page_Load(object sender, EventArgs e)
{
MethodInfo m = this.GetType().GetMethod(test); //expects static method
if (m != null)
{
object result = m.Invoke(this, new object[] { });
}
}
private static void ExecuteTest()
{
//do stuff
}
But no, you can't edit/pull data into the web.config on the fly. Changing the web.config will cause the application to restart killing all sessions. It's possible to build the web.config prior to starting but once it's up and running it's essentially locked down.

Add Entity Framework Model to Class Library

I have created a class library and added a EF Model but as soon as I declare a variable my project just skip the rest of my code without any error. I do not understand what is causing this to happen.
Library Code
public class Phisc
{
//Global DB Entity variable
live_restoreEntities db = new live_restoreEntities();
//Write data to file
public void PhiscFile(int value)
{
string strFileName, strFilePath;
StreamWriter stm;
//Create a file name that will be created
strFileName = DateTime.Now.ToString("yyyyMMddHHmmss") + "_PHISC";
//The path that were the file will be saved
strFilePath = "c:\\" + strFileName + ".txt";
//Validate if file exists
if (!System.IO.File.Exists(strFilePath))
System.IO.File.Create(strFilePath).Dispose();
stm = new StreamWriter(strFilePath, false);
stm.Write("This is a test message from C#");
stm.Close();
}
}
WinForm Code
private void Form1_Load(object sender, EventArgs e)
{
Phisc.Phisc pFile = new Phisc.Phisc();
pFile.PhiscFile(14);
}
When I create a instance of the library it does not hit my PhiscFile Method.
I have added a breakpoint to it and it stops at this constructor
public live_restoreEntities() : base("name=live_restoreEntities", "live_restoreEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
I am using a windows application to test my library
The parameterless constructor goes out and look for the conenctionstring in the App.config file. It look next to the .exe file.
I'm guessing that you need to include your App.config (from your entity library) to your WinForms library.
In the App.config, it should look like this:
<configuration>
<connectionStrings>
<add name="live_restoreEntities"
connectionString="<your connection string here>"
providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>

Using default constructor and parameterised constructor in unity c#

I have an application that uses unity FW to resolve objects throughout.
I have done some change to the framework and the classes which can be seen in the code comment as "NEW CHANGE"
The wrapper class looks like
public static class ContractResolver
{
public static T Resolve<T>() //This is been used in many places in application
{
IUnityContainer container = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
return container.Resolve<T>();
}
//NEW CHANGE: This is the new function that suppose to return the instance of parameterised constructor
public static T Resolve<T>(ParameterOverride[] parameterOverrides)
{
IUnityContainer container = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
return container.Resolve<T>(parameterOverrides);
}
}
The configuration looks like
<unity>
<containers>
<container>
<types>
<type type ="UnityTest.IImageRepositoryService, UnityTest" mapTo="UnityTest.ImageRepositoryService, UnityTest"/>
</types>
</container>
</containers>
</unity>
The classes and interface looks like
public interface IImageRepositoryService
{
bool Exists(string imageName);
}
public class ImageRepositoryService : IImageRepositoryService
{
private readonly string mFilterName = "StandardImageFilter";
//[InjectionConstructor]
public ImageRepositoryService()
{
DatabaseQueryProvider.Query("Image", mFilterName);
}
//NEW CHANGE. A CONSTRUCTOR THAT ACCEPTS A PARAMETER
//[InjectionConstructor]
public ImageRepositoryService(string filterName)
{
mFilterName = filterName;
DatabaseQueryProvider.Query("Image", filterName);
}
public bool Exists(string imageName)
{
Console.WriteLine("The image " + imageName + " found in filter " + mFilterName);
return true;
}
}
The usage looks like
var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();
serviceDefault.Exists("myimage.bmp");
The new changes breaks the old usage. i.e.
var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();
Throws exception
Resolution of the dependency failed, type = "UnityTest.IImageRepositoryService", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.
I would like to have the new functionality at the same time do not want to break the old functionality.
var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();
serviceDefault.Exists("myimage.bmp");
Should display the message in the console "The image myimage.bmp found in filter StandardImageFilter"
var parameterOverride1 = new ParameterOverride("filterName", "filter1");
var servicefilter1 = ContractResolver.Resolve<IImageRepositoryService>(new[] { parameterOverride1 });
servicefilter1.Exists("myimage.bmp");
Should display the message in the console "The image myimage.bmp found in filter filter1"
var parameterOverride2 = new ParameterOverride("filterName", "filter2");
var servicefilter2 = ContractResolver.Resolve<IImageRepositoryService>(new[] { parameterOverride2 });
servicefilter2.Exists("myimage.bmp");
Should display the message in the console "The image myimage.bmp found in filter filter2"
How solve this problem?
If you want to resolve the same type (in this case IImageRepositoryService) but have different calls to Resolve invoke different constructors then you will need to use named registrations.
In your case you can do this in the XML configuration:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type ="UnityTest.IImageRepositoryService, UnityTest" mapTo="UnityTest.ImageRepositoryService, UnityTest">
<constructor />
</register>
<register name="ParameterizedRepository"
type="UnityTest.IImageRepositoryService, UnityTest"
mapTo="UnityTest.ImageRepositoryService, UnityTest">
<constructor>
<param name="filterName" value="dummyValue" />
</constructor>
</register>
</container>
</unity>
Note, that I've used the Unity 2 (and 3) configuration style.
So this tells Unity that when resolving using the name "ParameterizedRepository" to invoke the constructor with the parameter named "filterName". I've used a dummy value here because we are going to override the value at runtime anyway:
var imageRepositoryService = container.Resolve<IImageRepositoryService>(
"ParameterizedRepository",
new ParameterOverride("filterName", "filter2"));
So that's how to get what you want using Unity so in terms of your wrapper class you should add a name parameter:
public static class ContractResolver
{
//NEW CHANGE: This is the new function that suppose to return the instance of parameterised constructor
public static T Resolve<T>(string name, params ParameterOverride[] parameterOverrides)
{
IUnityContainer container = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
return container.Resolve<T>(name, parameterOverrides);
}
}
A few unsolicited comments (in the spirit of trying to be helpful):
It looks like you are using Unity version 1. If so, you might want to consider upgrading (version 3 was released recently) and if you are not using Unity version 1, then you might want to consider changing the XML configuration syntax to use the newer approach as well as using the LoadConfiguration() extension method.
I'm not sure why every call to ContractResolver.Resolve() creates a new Unity container and then loads the configuration. This could be a bit of a performance hit. Usually, you would create a container and load the configuration once and use that instance for the lifetime of the application.
I can understand how you would be tempted to hide the container implementation behind the ContractResolver but with the addition of ParameterOverride (which are Unity specific) the abstraction is becoming a bit leaky.

Variables within app.config/web.config

Is it is possible to do something like the following in the app.config or web.config files?
<appSettings>
<add key="MyBaseDir" value="C:\MyBase" />
<add key="Dir1" value="[MyBaseDir]\Dir1"/>
<add key="Dir2" value="[MyBaseDir]\Dir2"/>
</appSettings>
I then want to access Dir2 in my code by simply saying:
ConfigurationManager.AppSettings["Dir2"]
This will help me when I install my application in different servers and locations wherein I will only have to change ONE entry in my entire app.config.
(I know I can manage all the concatenation in code, but I prefer it this way).
A slightly more complicated, but far more flexible, alternative is to create a class that represents a configuration section. In your app.config / web.config file, you can have this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This section must be the first section within the <configuration> node -->
<configSections>
<section name="DirectoryInfo" type="MyProjectNamespace.DirectoryInfoConfigSection, MyProjectAssemblyName" />
</configSections>
<DirectoryInfo>
<Directory MyBaseDir="C:\MyBase" Dir1="Dir1" Dir2="Dir2" />
</DirectoryInfo>
</configuration>
Then, in your .NET code (I'll use C# in my example), you can create two classes like this:
using System;
using System.Configuration;
namespace MyProjectNamespace {
public class DirectoryInfoConfigSection : ConfigurationSection {
[ConfigurationProperty("Directory")]
public DirectoryConfigElement Directory {
get {
return (DirectoryConfigElement)base["Directory"];
}
}
public class DirectoryConfigElement : ConfigurationElement {
[ConfigurationProperty("MyBaseDir")]
public String BaseDirectory {
get {
return (String)base["MyBaseDir"];
}
}
[ConfigurationProperty("Dir1")]
public String Directory1 {
get {
return (String)base["Dir1"];
}
}
[ConfigurationProperty("Dir2")]
public String Directory2 {
get {
return (String)base["Dir2"];
}
}
// You can make custom properties to combine your directory names.
public String Directory1Resolved {
get {
return System.IO.Path.Combine(BaseDirectory, Directory1);
}
}
}
}
Finally, in your program code, you can access your app.config variables, using your new classes, in this manner:
DirectoryInfoConfigSection config =
(DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved; // This value will equal "C:\MyBase\Dir1"
You can accomplish using my library Expansive. Also available on nuget here.
It was designed with this as a primary use-case.
Moderate Example (using AppSettings as default source for token expansion)
In app.config:
<configuration>
<appSettings>
<add key="Domain" value="mycompany.com"/>
<add key="ServerName" value="db01.{Domain}"/>
</appSettings>
<connectionStrings>
<add name="Default" connectionString="server={ServerName};uid=uid;pwd=pwd;Initial Catalog=master;" provider="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Use the .Expand() extension method on the string to be expanded:
var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
or
Use the Dynamic ConfigurationManager wrapper "Config" as follows (Explicit call to Expand() not necessary):
var serverName = Config.AppSettings.ServerName;
// returns "db01.mycompany.com"
var connectionString = Config.ConnectionStrings.Default;
// returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
Advanced Example 1 (using AppSettings as default source for token expansion)
In app.config:
<configuration>
<appSettings>
<add key="Environment" value="dev"/>
<add key="Domain" value="mycompany.com"/>
<add key="UserId" value="uid"/>
<add key="Password" value="pwd"/>
<add key="ServerName" value="db01-{Environment}.{Domain}"/>
<add key="ReportPath" value="\\{ServerName}\SomeFileShare"/>
</appSettings>
<connectionStrings>
<add name="Default" connectionString="server={ServerName};uid={UserId};pwd={Password};Initial Catalog=master;" provider="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Use the .Expand() extension method on the string to be expanded:
var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
Good question.
I don't think there is. I believe it would have been quite well known if there was an easy way, and I see that Microsoft is creating a mechanism in Visual Studio 2010 for deploying different configuration files for deployment and test.
With that said, however; I have found that you in the ConnectionStrings section have a kind of placeholder called "|DataDirectory|". Maybe you could have a look at what's at work there...
Here's a piece from machine.config showing it:
<connectionStrings>
<add
name="LocalSqlServer"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
Usally, I end up writing a static class with properties to access each of the settings of my web.config.
public static class ConfigManager
{
public static string MyBaseDir
{
return ConfigurationManager.AppSettings["MyBaseDir"].toString();
}
public static string Dir1
{
return MyBaseDir + ConfigurationManager.AppSettings["Dir1"].toString();
}
}
Usually, I also do type conversions when required in this class. It allows to have a typed access to your config, and if settings change, you can edit them in only one place.
Usually, replacing settings with this class is relatively easy and provides a much greater maintainability.
I thought I just saw this question.
In short, no, there's no variable interpolation within an application configuration.
You have two options
You could roll your own to substitute variables at runtime
At build time, massage the application configuration to the particular specifics of the target deployment environment. Some details on this at dealing with the configuration-nightmare
You have a couple of options. You could do this with a build / deploy step which would process your configuration file replacing your variables with the correct value.
Another option would be to define your own Configuration section which supported this. For example imagine this xml:
<variableAppSettings>
<variables>
<add key="#BaseDir" value="c:\Programs\Widget"/>
</variables>
<appSettings>
<add key="PathToDir" value="#BaseDir\Dir1"/>
</appSettings>
</variableAppSettings>
Now you would implement this using custom configuration objects which would handle replacing the variables for you at runtime.
You can use environment variables in your app.config for that scenario you describe
<configuration>
<appSettings>
<add key="Dir1" value="%MyBaseDir%\Dir1"/>
</appSettings>
</configuration>
Then you can easily get the path with:
var pathFromConfig = ConfigurationManager.AppSettings["Dir1"];
var expandedPath = Environment.ExpandEnvironmentVariables(pathFromConfig);
Inside <appSettings> you can create application keys,
<add key="KeyName" value="Keyvalue"/>
Later on you can access these values using:
ConfigurationManager.AppSettings["Keyname"]
I would suggest you DslConfig. With DslConfig you can use hierarchical config files from Global Config, Config per server host to config per application on each server host (see the AppSpike).
If this is to complicated for you you can just use the global config Variables.var
Just configure in Varibales.var
baseDir = "C:\MyBase"
Var["MyBaseDir"] = baseDir
Var["Dir1"] = baseDir + "\Dir1"
Var["Dir2"] = baseDir + "\Dir2"
And get the config values with
Configuration config = new DslConfig.BooDslConfiguration()
config.GetVariable<string>("MyBaseDir")
config.GetVariable<string>("Dir1")
config.GetVariable<string>("Dir2")
I don't think you can declare and use variables to define appSettings keys within a configuration file. I've always managed concatenations in code like you.
I'm struggling a bit with what you want, but you can add an override file to the app settings then have that override file set on a per environment basis.
<appSettings file="..\OverrideSettings.config">
For rolling out products where we need to configure a lot of items with similar values, we use small console apps that read the XML and update based on the parameters passed in. These are then called by the installer after it has asked the user for the required information.
I would recommend following Matt Hamsmith's solution. If it's an issue to implement, then why not create an extension method that implements this in the background on the AppSettings class?
Something like:
public static string GetValue(this NameValueCollection settings, string key)
{
}
Inside the method you search through the DictionaryInfoConfigSection using Linq and return the value with the matching key. You'll need to update the config file though, to something along these lines:
<appSettings>
<DirectoryMappings>
<DirectoryMap key="MyBaseDir" value="C:\MyBase" />
<DirectoryMap key="Dir1" value="[MyBaseDir]\Dir1"/>
<DirectoryMap key="Dir2" value="[MyBaseDir]\Dir2"/>
</DirectoryMappings>
</appSettings>
I came up with this solution:
In the application Settings.settings I defined a variable ConfigurationBase (with type=string Scope=Application)
I introduced a variable in the target attributes in the Settings.settings, all those attributes had to be set to Scope=User
In the app.xaml.cs I read out the value if the ConfigurationBase
In the app.xaml.cs I replaced all variables with the ConfigurationBase value. In order to replace the values at run-time the attributes had to be set to Scopr=User
I'm not really happy with this solution because I have to change all attributes manually, if I add a new one I have to regard it in the app.xaml.cs.
Here a code snippet from the App.xaml.cs:
string configBase = Settings.Default.ConfigurationBase;
Settings.Default.CommonOutput_Directory = Settings.Default.CommonOutput_Directory.Replace("${ConfigurationBase}", configBase);
UPDATE
Just found an improvement (again a code snippet from the app.xaml.cs):
string configBase = Settings.Default.ConfigurationBase;
foreach (SettingsProperty settingsProperty in Settings.Default.Properties)
{
if (!settingsProperty.IsReadOnly && settings.Default[settingsProperty.Name] is string)
{
Settings.Default[settingsProperty.Name] = ((string)Settings.Default[settingsProperty.Name]).Replace("${ConfigurationBase}", configBase);
}
}
Now the replacements work for all attributes in my settings that have Type=string and Scope=User. I think I like it this way.
UPDATE2
Apparently setting Scope=Application is not required when running over the properties.
Three Possible Solutions
I know I'm coming late to the party, I've been looking if there were any new solutions to the variable configuration settings problem. There are a few answers that touch the solutions I have used in the past but most seem a bit convoluted. I thought I'd look at my old solutions and put the implementations together so that it might help people that are struggling with the same problem.
For this example I have used the following app setting in a console application:
<appSettings>
<add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
<add key="StaticClassExample" value="bin"/>
<add key="InterpollationExample" value="{0}bin"/>
</appSettings>
1. Use environment variables
I believe autocro autocro's answer touched on it. I'm just doing an implementation that should suffice when building or debugging without having to close visual studio. I have used this solution back in the day...
Create a pre-build event that will use the MSBuild variables
Warning: Use a variable that will not be replaced easily so use your project name or something similar as a variable name.
SETX BaseDir "$(ProjectDir)"
Reset variables; using something like the following:
Refresh Environment Variables on Stack Overflow
Use the setting in your code:
'
private void Test_Environment_Variables()
{
string BaseDir = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
string ExpandedPath = Environment.ExpandEnvironmentVariables(BaseDir).Replace("\"", ""); //The function addes a " at the end of the variable
Console.WriteLine($"From within the C# Console Application {ExpandedPath}");
}
'
2. Use string interpolation:
Use the string.Format() function
`
private void Test_Interpollation()
{
string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, #"..\..\"));
string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
Console.WriteLine($"Using old interpollation {ExpandedPath}");
}
`
3. Using a static class, This is the solution I mostly use.
The implementation
`
private void Test_Static_Class()
{
Console.WriteLine($"Using a static config class {Configuration.BinPath}");
}
`
The static class
`
static class Configuration
{
public static string BinPath
{
get
{
string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, #"..\..\"));
return SolutionPath + ConfigPath;
}
}
}
`
Project Code:
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
<add key="StaticClassExample" value="bin"/>
<add key="InterpollationExample" value="{0}bin"/>
</appSettings>
</configuration>
Program.cs
using System;
using System.Configuration;
using System.IO;
namespace ConfigInterpollation
{
class Program
{
static void Main(string[] args)
{
new Console_Tests().Run_Tests();
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
internal class Console_Tests
{
public void Run_Tests()
{
Test_Environment_Variables();
Test_Interpollation();
Test_Static_Class();
}
private void Test_Environment_Variables()
{
string ConfigPath = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
string ExpandedPath = Environment.ExpandEnvironmentVariables(ConfigPath).Replace("\"", "");
Console.WriteLine($"Using environment variables {ExpandedPath}");
}
private void Test_Interpollation()
{
string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, #"..\..\"));
string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
Console.WriteLine($"Using interpollation {ExpandedPath}");
}
private void Test_Static_Class()
{
Console.WriteLine($"Using a static config class {Configuration.BinPath}");
}
}
static class Configuration
{
public static string BinPath
{
get
{
string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, #"..\..\"));
return SolutionPath + ConfigPath;
}
}
}
}
Pre-build event:
Project Settings -> Build Events

Categories

Resources