I have a solution which has multiple output projects (a website, and admin tool, and a SOAP API layer).
They each share common projects in the solution (the service layer, data layer etc). In one of these common projects, I am looking to store a config layer.
Right now, we have three seperate appsettings config files for each output project -
development.AppSettings.config
testing.AppSettings.config
production.AppSettings.config
So altogether, there are nine config files. Only one is used in each project, as they are referenced by utilising the configSource attribute in the web.config appsettings node.
Anyhoo, it's getting to be a pain any time we want to add/remove values from our config files, because it means that we have to change all nine files to do this. And here's what I'd like to do:
In the common project, we have three config files as above. These would be set to copy to the output directory, so that each project has a copy of them. These would be the 'base' config.
Then in each project, I would like to have three files again, but they wouldn't necessarily have to contain the same values as the base configs. If they did however, then the base config value would be overridden by the value in the output project config. A form of configuration inheritance, I suppose.
On application start, I'd like to be able to get these two config files - the base config, and the project config file. And then set the app settings accordingly.
What I'm wondering though, is what's a nice way of determining which file to use? Also, I'm wondering if this is a good way of sharing application values across a large solution, and if there's another, perhaps more efficient way of doing it?
If I'm in development mode, then I don't want production.appsettings.config, and vice versa if I'm in production mode.
Is there a simple way to get the mode (development/testing/production) that I'm in before I go off and get the configurations?
You can have one set of files (3 configs) and link/share them in whatever projects you need.
http://www.devx.com/vb2themax/Tip/18855
Hope this helps.
You could use the ConfigurationManager.OpenExeConfiguration static method. This will allow you to work with as many config files as you want.
You may also try creating a custom class to store all of your settings. You could then serialize your object to save it as a file. You could extend your base custom config class to fit all your other projects.
After some careful thought, and a trip to the toilet at 03:30, I came across a solution which works.
Let's say that we have some appSettings in our base config file:
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="MyValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
And in my output project, I have three appSettings:
<!-- This is used to identify which config to use. -->
<add key="Config" value="Development" />
<!-- Different value to the one in the base -->
<add key="MyKey2" value="NewValue2" />
<!-- This key does not exist in the base config -->
<add key="MyKey6" value="MyValue6" />
In my Application_Start, I have a call to GetConfigs():
ConfigHelper.GetConfig(HostingEnvironment.MapPath("~/bin/BaseConfig"));
And the actual GetConfigs function:
public static void GetConfigs()
{
if (configMode == null)
{
configMode = ConfigurationManager.AppSettings.Get("Config").ToLowerInvariant();
}
//Now load the app settings file and retrieve all the config values.
var config = XElement.Load(#"{0}\AppSettings.{1}.config".FormatWith(directory, configMode))
.Elements("add")
.Select(x => new { Key = x.Attribute("key").Value, Value = x.Attribute("value").Value })
//If the current application instance does not contain this key in the config, then add it.
//This way, we create a form of configuration inheritance.
.Where(x => ConfigurationManager.AppSettings.Get(x.Key) == null);
foreach (var configSetting in config)
{
ConfigurationManager.AppSettings.Set(configSetting.Key, configSetting.Value);
}
}
Now, my output project effectively has the following configuration settings:
<add key="Config" value="Development" />
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="NewValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
<add key="MyKey6" value="MyValue6" />
Simples!
Related
I currently am writing to a statically named log file as defined in my serilog config:
<add key="serilog:write-to:File.path" value="%LOCALAPPDATA%\App_Data\Logs\StaticallyNamedLog.log" />
I'd like to be able to change the logfile name at runtime i.e.
<add key="serilog:write-to:File.path" value="%LOCALAPPDATA%\App_Data\Logs\{appname}-Log.log" />
In log4net for example I'd set a log4net global property value for "appname" in code before the settings were read in.
log4net.globalContext.properties["appname"] = "App1"
log4net.config.xmlConfigurator.configure("log4net.config")
Then in the logfile I'd reference it like so
%property{appname}
This would then get substituted into this path.
How can I do the equivalent in Serilog? I've done a lot of searching but can't find anything on this specific problem.
Thanks
I've searched around and no answers have worked for me. I have 2 ASP.NET projects, one of them being my project, the other being a project containing unit tests.
# Projects
Project.Core
...
secretConnectionStrings.config
Web.config
Project.Tests
In Project.Core, I have a method that requests both a connection string (from secretConnectionStrings.config and an app setting from the web.config file. Executing on it's own, the correct values are pulled from both of these config files. However, I want to test this within my unit test.
My test looks like this (within Project.Tests):
[TestClass]
public class TestUserController
{
[TestMethod]
public void Post_ShouldReturnNewUser()
{
var controller = new UserController();
var result = controller.Post() as User;
Assert.AreNotEqual(result, null);
}
}
The line it bombs out on is this
string cacheConnection = ConfigurationManager.ConnectionStrings["RedisCache"].ConnectionString;
System.NullReferenceException: Object reference not set to an instance of an object
I've tried adding web.config and secretConnectionStrings.config as a link in my Project.Tests solution. That hasn't worked, and I do not want to interface to my web.config (I want the values as they are saved in Project.Core). Is there a solution where I can pull values from Project.Core's web.config into my unit test?
The project containing the unit tests will need its own config file for the ConfigurationManager to pull in the connection strings. The best way to set this up and the keep only 1 copy of the config file is to add the same file into each project that needs it, but add it as a linked file:
Don't forget to set the file properties so that it deploys with the executable code:
And add it as a deployment item to your test fixture(s):
[TestClass]
[DeploymentItem(#"db.config")]
public class MyTestFixture { ... }
Now, if you want to have centralized connection strings in two differently-named config files (app.config & web.config), then you can place them in a third file db.config and import that file into each of the other config files:
db.config
<connectionStrings>
<add name="testDb" connectionString="data source=(localdb)\v11.0;..."
providerName="System.Data.SqlClient" />
</connectionStrings>
app.config / web.config
<connectionStrings configSource="db.config" />
Make sure to set Copy To Output Directory for the imported config files in addition to your app.config or web.config and add them as deployment items to the test fixture with the attribute.
I need to modify the <configuration><system.diagnostics> section of an app.config at runtime so that I can:
add a CustomTraceListener under the <sharedListeners> element, which requires special initializeData that can only be ascertained at runtime.
add the CustomTraceListener shared listener to an existing source under the <source><listeners> element.
persist the CustomTraceListener to other assemblies which load their trace source and listener configurations from the config file.
The relevant sections in app.config looks something like this presently:
<system.diagnostics>
<sources>
<source name="mysource" switchName="..." switchType="...">
<listeners>
<add name="console" />
<add name="customtracelistener" /> /// need to add new listener here
</listeners>
</source>
</sources>
<sharedListeners>
<add name="console" type="..." initializeData="..." />
<add name="customtracelistener" type="..." initializeData="..."> /// need to add custom trace listener here
<filter type="..." initializeData="Warning"/> /// with a filter
</add>
</sharedListeners>
<switches>
<add name="..." value="..." />
</switches>
</system.diagnostics>
Using ConfigurationManager I can easily do:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection diagnostics = config.GetSection("system.diagnostics");
And when I do this, diagnostics is a System.Diagnostics.SystemDiagnosticsSection type. Interestingly I can't cast diagnostics to a SystemDiagnosticsSection type because I can't find it within any namespace. Regardless, ConfigurationSection doesn't seem to have any methods that I can use to write data into the section.
I also can't cast it to a NameValueConfigurationCollection because diagnostics base type is ConfigurationSection. I heard about this technique but it seems I can't use it.
Do I have to revert to using plain-old XML to accomplish this? I really don't like reinventing the wheel.
You can locate the path to the app.exe.config file through the ConfigurationManager, then load the config file as an XDocument.
string configPath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
XDocument config = XDocument.Load(configPath);
XElement diagnostics = config.Descendants().FirstOrDefault(e => e.Name == "system.diagnostics");
if (diagnostics == default(XElement))
{
/// <system.diagnostics> element was not found in config
}
else
{
/// make changes within <system.diagnostics> here...
}
config.Save(configPath);
Trace.Refresh(); /// reload the trace configuration
Once the required changes are made, save the XDocument back to disk, and call Trace.Refresh() to reload the trace configuration.
See MSDN regarding the Trace.Refresh method here.
For experience i would warn you to make app.config changes from application if the app is deployed with a good install procedure under protected directories, eg. Program files in MS OS with UAC activated.
To update config files sometimes you need some admin privileges.
The Bad thing is than all run correctly under visual studio / debug or some test procedure, after deploy, in production,you may have some issue...
If you make direct changes to the <configuration><system.diagnostics>
section of the app.config file at run time, the app needs to be restarted or Trace.Refresh() must be called to have the changes take effect.
Another option is to programmatically add TraceListeners at application start-up, e.g.
Trace.Listeners.Add(new TextWriterTraceListener("output.log", "myListener"));
see https://msdn.microsoft.com/en-us/library/sk36c28t(v=vs.110).aspx.
To add a filter with the initializeData value as in your question you can use the TraceListener.Filter property
To share settings across applications you can use the configSource property on the <system.diagnostics> element and put that element in a separate config file. The downside of this is that the file needs to be in the same folder as the app.config. So a change to one file would either need to be copied and pasted to other locations or shared in some other way.
An alternative would be to save a custom config file containing the trace listener information at a location all apps can access and then in each app load the file at start-up and configure the trace listeners as above.
Update
To share logging throughout your application you could create a class that implements the singleton pattern to return your TraceSource instance or to wrap your logging activities. This way you don't have to pass round the same instance
public class Logger
{
private static Logger _logger;
private TraceSource _ts;
private Logger()
{
// Configure TraceSource here as required, e.g.
_ts = new TraceSource("StackOverflow", SourceLevels.All);
_ts.Listeners.Add(new TextWriterTraceListener(#"c:\temp\tracefile.log"));
}
public static Logger Get()
{
if (_logger == null)
{
_logger = new Logger();
}
return _logger;
}
public void TraceInfo(string message)
{
_ts.TraceInformation(message);
_ts.Flush();
}
}
// To use
Logger.Get().TraceInfo("This is a trace message");
You could extend this to then encapsulate the actual messages you want to log so that the code doing the logging doesn't know the specifics and you have just one place where your events are defined, e.g.
public void TraceApplicationStarting()
{
_ts.TraceEvent(TraceEventType.Verbose, 1, "Application Starting");
}
Is it possible to retrieve a custom configuration section from a config file other than the app.config or web.config.
I tried using the System.Configuration.ConfigurationManager's OpenExeConfiguration and GetSection method calls together but without luck. My intention is to define custom configuration sections for interchangeable process adapters and contain the custom config section in a separate config file other than app.config and web.config. I see plenty of examples for appsettings and connectionstrings.
static private DigiKeyReadTaskConfigSection digiKeyReadTaskConfigSection;
static DigiKeyReadTaskConfigSection DigiKeyReadTaskConfigSection {
get {
if (digiKeyReadTaskConfigSection == null) {
digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection");
}
return digiKeyReadTaskConfigSection;
}
}
The digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration call seems to be working however the (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection") returns null.
The ReadTask.config file lives in the bin file of the App:
<configuration> <configSections>
<section name="DigiKeyReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.DigiKeyReadTaskConfigSection, DataReadInc.WebSiteRead" />
<section name="ArrowReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.ArrowReadTaskConfigSection, DataReadInc.WebSiteRead" /> </configSections> <DigiKeyReadTaskConfigSection DigiKeySiteURL="http://search.digikey.com/scripts/DkSearch/dksus.dll?WT.z_header=search_go&lang=en&site=us&keywords="
SiteLogInURL="https://ordering.digikey.com/RegisteredUser/Login.aspx,formName=" SiteLoginId="X" SiteLoginPassword="X" /> <ArrowReadTaskConfigSection ArrowAmericaSiteURL="http://components.arrow.com/part/search/"
SiteLoginURL="http://components.arrow.com/login/processlogin#" SiteLoginId="X" SiteLoginPassword="X" /> </configuration>
I have seen this type of setup with Spring.Net and a J2EE implementation so I am sure it is possible. I can just put my custom config sections in the App.config or web.config file, however it would be significantly cleaner for them to exist in their own config file.
Use ConfigurationManager.OpenMappedExeConfiguration(). OpenExeConfiguration relates to a certain exe.
I have the following two profile properties in my web.config file between the System.Web tags:
<anonymousIdentification enabled="true"/>
<profile>
<properties>
<add name="CustomerName" allowAnonymous="true"/>
<add name="CustomerID" allowAnonymous="true"/>
</properties>
</profile>
When I try to access the Profile.CustomerName or Profile.CustomerID from an aspx.cs file, it doesn't show up. I thought when you create a custom profile property, it automatically updates the aspnetdb with the new properties. Profile doesn't show up in intellisense as well.
This problem occurs if you are creating a Web Application. Web Apps, unlike Websites, don't generate the ProfileCommon class.
You could either follow this method:
http://webproject.scottgu.com/CSharp/Migration2/Migration2.aspx
(Appendix 2)
I haven't personally used the above assembly, so someone else might give some advice on that.
However, you could also try:
Object customerName = HttpContext.Current.Profile.GetPropertyValue("CustomerName")
You won't get intellisense with this method, but as long as you know the property name, it can be accessed like that.
Specify properties in web.config file as follows : -
Now, write the following code : -
Compile and run the code. You will get following output: -