App.config multi-project access strategies - c#

My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.

I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections

ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.

Related

How can a C# library detect if app.config exists and manually load a config file if not?

I have a static C# library project which relies on configuration. This library may be used in two scenarios:
From managed C# applications. Easy - the application's app.config file will be used, via ConfigurationManager
From unmanaged C++ applications (using COM)
In case 2. there is no app.config. I would like the library to still be able to use ConfigurationManager but it should explicitly load an XML config file with the same structure as app.config.
This question talks about how to manually load a config file: Loading custom configuration files
But how can the library detect which case 1/2 its in? I would be happy with an InitLib method which passes a config name, or a static initializer, but I can't see how to put the pieces together.
The config file name to use in case 2 could either be passed in directly, or hardcoded as MyAssembly.Lib.config or similar.
So to clarify: you have a class in a class library, the library references System.Configuration.ConfigurationManager.dll, and the class looks does something like this:
using System.Configuration;
namespace FooLibrary
{
public class Foo
{
public Foo()
{
var bar = ConfigurationManager.AppSettings("FooLibrary.Foo.Bar");
}
}
}
Now when you call this class from a console application, say, Baz.exe, there will exist a Baz.exe.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="FooLibrary.Foo.Bar" value="Baz" />
</appSettings>
</configuration>
And all works.
It doesn't work when the library runs in a context without a configuration file. While I think it's possible to give an unmanaged application, say, Qux.exe a configuration file Qux.exe.config which .NET will then read for assemblies loaded from that executable, that situation isn't ideal. Even if that'd work (I think, but am not sure, that it's just a file name convention, not something the runtime does for executables on startup). It's possible that the executable running the assembly is not under your control anyway (e.g. somewhere in System32).
While you could let the library load a configuration file relative to its own location, and to answer your question:
But how can the library detect which case 1/2 its in?
You could just test for the AppSettings key anyway, and if not found, assume you've got no configuration file, and open that of the DLL instead:
public class Foo
{
private readonly string _bar;
public Foo()
{
// Read from .exe.config or Web.config
_bar = ConfigurationManager.AppSettings("FooLibrary.Foo.Bar");
if (string.IsNullOrEmpty(_bar))
{
// Assume that if not present, own config must be loaded:
var dllConfigPath = Assembly.GetExecutingAssembly().Location;
// Will just append ".config" despite its name
Configuration config = ConfigurationManager.OpenExeConfiguration(dllConfigPath);
_bar = config.AppSettings.Settings["test"]?.Value;
if (string.IsNullOrEmpty(_bar))
{
// Not found in both configs, now what?
}
}
}
}
And you should refactor it a bit to read multiple variables. And now you want to unit test this code, so you'll have to provide a configuration file there as well.
These problems are solved by not reading the configuration in class libraries, but in application startup, and passing the appropriate values to the constructor of the classes from the library:
public Foo(string bar) { ... }
// And then from your executable:
var foo = new Foo(ConfigurationManager.AppSettings["FooLibrary.Foo.Bar"));
// And your unit test
var foo = new Foo("FromTest");
But you can't pass constructor parameters through COM, which the compiler will tell you as well. So you'll have to provide a parameterless constructor, and something like an InitLib(string bar) method for COM only.
And if you, despite all the above, still insist on reading the config there, then it would look something like this:
public class Foo
{
private string _bar;
public Foo()
{
// Read from .exe.config or Web.config
_bar = ConfigurationManager.AppSettings["FooLibrary.Foo.Bar"];
// Can't throw here if _bar is empty, maybe we're called from COM
}
public void ReadDllConfig()
{
// Assume that if not present, own config must be loaded:
var dllConfigPath = Assembly.GetExecutingAssembly().Location;
// Will just append ".config" despite its name
Configuration config = ConfigurationManager.OpenExeConfiguration(dllConfigPath);
_bar = config.AppSettings.Settings["test"]?.Value;
// Can throw here if _bar is empty
}
public void FooForCom()
{
// TODO: test _bar again, it can still be null.
}
}

Winform app: Compiled App.Config-like file?

I would like to know if there is some kind of built-in compiled "App.Config" file?
The goal is to be able to have one of our library which can have some of its default values overriden when being used in some client application.
Thoses DLL are loaded dynamically, so I cannot just give a parameter in the constructor.
I don't want to use the App.config file because the user can edit those values(otherwise it would have been just fine).
There are several different ways to solve this.
If you like the idea of config-files, but do not want to have it accessible by end users in the compiled application, perhaps you can create your own settings-file in a format that suits your needs, and include it as an embedded resource?
An upside of this would be that you can access it as a regular XML or config file or whatever in Visual Studio, while it will be hidden from the end user. Personally I think I would prefer this to using special code / classes to store config-data.
To include a file as an embedded resource, include it into one of your Visual Studio projects, right click the included file and select Properties. Now under Build Action, select Embedded Resource. When you build your project now, the file will be included internally in the produced .dll-file.
I'm sure you'll be able to find lot's of info about how to access an embedded resource from code. As an example, there are some useful examples in this SO question. Note especially this answer, which also mentions an alternative way to include a resource.
Expanding on my comment... you could just make an interface for a settings class with hardcoded values, and then make different implementations of that interface. To actually change which one to use, all you'd need to do is comment/uncomment the line that instantiates an object into your settings variable before you build the dll:
public class MainDllProject
{
ISettings m_Settings;
public MainDllProject()
{
// Change this before compiling
this.m_Settings = new DebugSettings();
//this.m_Settings = new DeploySettings();
// use settings from the settings class
String setting1 = this.m_Settings.Setting1
Int32 setting2 = this.m_Settings.Setting2
//...
}
}
public interface ISettings
{
String Setting1 { get; }
Int32 Setting2 { get; }
}
public class DebugSettings: ISettings
{
public String Setting1
{ get { return "data_debug";} }
public Int32 Setting2
{ get { return 2;} }
}
public class DeploySettings: ISettings
{
public String Setting1
{ get { return "data_deploy";} }
public Int32 Setting2
{ get { return 1;} }
}
On finding "a built-in way of solving this", as you said, maybe this will be useful for you...
You can actually use the Visual Studio build configuration manager to build with different settings. Using the #If directives, you can automatically make it select which lines of code to use based on the configuration. A simple example based on the default debug configuration, which adds the "DEBUG=True" variable automatically:
public MainDllProject()
{
#If DEBUG Then
this.m_Settings = new DebugSettings();
#ElseIf
this.m_Settings = new DeploySettings();
#End if
}
You can actually make your own custom-named variables to check on just like that DEBUG one: after making a configuration, open the Project properties window, go to the Compile tab, select that specific configuration in the dropdown, and then at the bottom select "Advanced Compile Options". In there is a line "Custom constants" in which you can add such variables. For simple if-statements, you can just make a boolean like "CLIENTDEPLOY=True", and then you can use #If CLIENTDEPLOY Then in your code.

Accessing the configuration file across application domain

We are implementing a plug and play module for our application where user can load and unload the desired class library at runtime. So I have decided to use MEF and shadow copying of class libraries.
The thing here is each class library may have different configuration properties which needs to set by user. My main application has no knowledge about the configurations present in the class library.
Now the problem is when I try to transfer the application configuration file loaded with class library from one application domain to another.
Without MEF, I have just returned Settings.Default from the class library and I have used it in our main application to edit the settings. With MEF and shadow copying, It doesn't seems to be working because
The object type needs to known to both sides.
I cannot implement MarshalByRefObject on the settings file since
the settings file is already extending ApplicationSettingsBase which
is an abstract class and c# doesn't supports multiple inheritance.
Currently I am creating a class which holds all the properties as string and creating a GUI in my main application based on this class content.
public class ExtensionModuleConfiguration : MarshalByRefObject
{
public string Name { get; set; }
public string Value { get; set; }
public List<string> Options { get; set; }
public UIElements ToolUIElement { get; set; }
}
public enum UIElements
{
ComboBox,
TextBox
}
I must say this is not the best solution.
Can someone suggest a better way to set the configurations of a class library in MEF?
There two ways how you can do it. You must inform .NET which app.config should be loaded in the appdomain of your MEF plugin class.
Therefore you can either point particular app.config for your plugin DLL like this:
ConfigurationManager.OpenExeConfiguration("Plugin.dll");
var name = AppSettings.Settings["Name"].Value;
Or you can load the app.config for your main application DLL and put all the appsettings in that file. In this case you should do:
var config = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
var name = config.AppSettings.Settings["Name"].Value;
Both solutions should be called from within of you Plugin implementation for example in constructor. Or by first call to some lazy loaded configuration property.

Sharing settings between applications

I have multiple .NET assemblies that all need to share common user settings, such as preferences, user names, etc. One is a WPF application, another is a console application, and the third is an Office Add-in. All of these settings are user-scope.
Only the WPF application needs to be able to change settings. The rest just read them.
Ideally, I'd like to use the .NET configuration framework. I'm not sure how to do this though. If I add Settings to the WPF application, how can the other applications find the user.config file?
Is it just easier to create a class library and use IsolatedFileStorage and serialize my settings?
Any advice would be greatly appreciated.
You can implement your custom settings class, inheriting ApplicationSettingsBase. As a good start, you can add the default User Settings file to a sample project (Right click on the project -> Properties -> Settings -> This project does not contain a default settings file. Click here to create one.). Add a user-scoped setting and investigate the structure of the designer-generated Settings.Designer.cs file:
namespace ConsoleApplication1.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("John Doe")]
public string Name {
get {
return ((string)(this["Name"]));
}
set {
this["Name"] = value;
}
}
}
}
In your custom implementation, you will not be limited to the designer-generated access modifiers, so you can implement the Settings class as internal with internal setters, visible only to the needed assemblies, or whatever fits your needs.
Of course, you can always implement your custom serialize/deserialize mechanism, but you will lose the funcionality provided by ApplicationSettingsBase's Updgrade, Reload, and Reset methods. If you don't need any of these, this could be the cleaner approach.
I would recommend you to create service to provide and update user info and or preferences. It will be better architecture, cleaner solution and it will be easier to maintain and extend.

Changes in App.config do not reflect after restarting the application

I am using an app.config file to store the dynamic parameters of my application. The problem is, when I change a value in app.config file, and start the application, it doesn't load the new value from config file. Seems like the values in app.config file are being read and embedded in exe file only at compile time!
This is how I read the config file:
public class Helper
{
static Helper()
{
Foo = ConfigurationManager.AppSettings["Foo"];
}
public static string Foo { get; set; }
}
Am I missing something?
Are you sure you are changing the correct file? You don't want to change the app.config file, but the <exename>.exe.config file, in the same directory as the .exe
The app.config file is what you edit in the ide, but when you compile your app this file is renamed to <exename>.exe.config and copied to the output directory when you compile. The .exe looks for a file with the same name as itself with the .config extension when looking for the default configuration.
The static nature of your class and method may be causing you the issue. Maybe refactor it to the following...
public static class Helper
{
public static string Foo
{
get
{
return ConfigurationManager.AppSettings["Foo"];
}
}
}
Actually, thinking about it, it doesn't help you a great deal since ConfigurationManager.AppSettings["Foo"] is already (effectively) a static call - you're just adding another layer of abstraction that may well not be required.

Categories

Resources