Monday, March 09, 2009 6:44 AMI'm having a lot of trouble trying to find an easy way to do this without parsing the file as an XML directly. I have an application that has an app.config file. In that application, I load a DLL and run some methods inside. Now, if I use <appSettings> in the main app.config file, I can specify a second file to grab values from. If I use <applicationSettings>, I do not believe I can. Why? I want to be able to grab the DLL specific settings from the DLL code and the main program settings from the main code.
Say I have main.exe.config:
<?xml version="1.0"?> <configuration> <appSettings file="MyDLL.dll.config"> <add key="SocketIFacePlugIn" value="MyDLL.dll" /> <add key="UseSocketPlugin" value="true" /> </appSettings> <startup> <supportedRuntime version="v2.0.50727" /> </startup> </configuration>
If I then have the MyDLL.dll.config:
<?xml version="1.0"?> <configuration> <appSettings> <add key="Random" value="true"/> </appSettings> </configuration>
I can then make the call
from either the main program or from the code within My.DLL. This leaves the configuration wide open.
Now say I have the main.exe.config:
<?xml version="1.0"?> <configuration> <applicationSettings> <Main_Namespace.Properties.Settings> <setting name="UseSocketPlugin" serializeAs="String"> <value>True</value> </setting> <setting name="SocketIFacePlugIn" serializeAs="String"> <value>MPDEmulator.dll</value> </setting> </Main_Namespace.Properties.Settings> </applicationSettings> </configuration>
If I then have the MyDLL.dll.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <applicationSettings> <MyDLL_PlugIn.Properties.Settings> <setting name="Random" serializeAs="String"> <value>True</value> </setting> </MyDLL_PlugIn.Properties.Settings> </applicationSettings> </configuration>
I can make this call from the main class to access something:
Properties.Settings resourceSettings = new Properties.Settings(); bool UsingNetworkPlugin = resourceSettings.UseSocketPlugin;
Now here's the catch. If I try the same thing inside the DLL as such (note that each appears in different namespaces):
Properties.Settings resourceSettings = Properties.Settings.Default; bool Random = resourceSettings.Random;
I can only ever get the default defined value. If I try and change MyDLL.dll.config directly after the project has been built, I can never see the changes, only the defaults as defined above. However, I can see changes made if I use userSettings instead and use the Save() method. I would still like to be able to specify the settings from a separate file though.
I'd prefer to use scenario 2 since it's newer than the former and is more intuitive. I'd really prefer not to load and traverse the XML directly since this is shady and bad practice if there is a .Net method already in place. I'd even be happy if someone could help me find a way to insert the settings block into the main.exe.config file programatically in a simple way on the first run of the program. The idea is that the DLL is unknown. It is loaded as an assembly at runtime since it is a plug-in. Since it is a plug-in, I can't let Visual Studio insert the block for me; it has to be inserted programmatically if it is to be inserted into the main.exe.config at all.
Anyone have any ideas? Thanks!
Monday, March 09, 2009 2:10 PMModeratorappSettings is the 1.x application settings infrastructure. It supported inclusion of external files. ApplicationSettings is a v2.x feature and it does not. Therefore you can't do what you wanted directly.
ApplicationSettings is more than just a bunch of key-value pairs. When you look at the app version what you're actually seeing is the settings configuration. This consists of the setting name, its type and its default value. It just so happens that the default value is also the application-level value until deployment. The designer will keep the config section and the auto-generated Properties class in sync with this regard. Once you build the default value is persisted in metadata so changing the config file won't impact it.
Starting to see where file inclusions will fail? Because the designer keeps the section and the code in sync it can't (at dev time) fetch values from other config files to produce the class that gets compiled. Technically it could I guess but remember that the runtime filename is different than the devtime filename so managing the differences would introduce some ugliness to the framework (where this code resides).
There are alternatives though. The first (and perhaps recommended) approach is to not use AS for your plugins. I suspect that your plugins can add their own settings. It doesn't make any sense at compile time to use AS because you won't know until runtime what the settings are for a plugin. So you're not gaining anything by using it. You can still use it for your application-specific settings but for plugins you should probably just stick with the standard configuration subsystem sections.
Another alternative is to use the IPersistComponentSettings interface in your plugins. This allows your plugins to load and save their settings into the main settings element. However all this is really doing is modifying the core config files. You still won't see them (as properties) in the Properties class. You also won't be able to ship a config file with your plugins with the settings pre-configured. You'll have to programmatically default them when needed.
Perhaps the best option, in my opinion, is to create a custom plugins section handler. Each plugin can then create its own plugin element with its own set of settings. When you load a plugin you can check for the existence of a plugin config. If you find it then you can merge the two XML files together (or more specifically you can merge just the plugin section). Then rename or delete the plugin config so it won't get loaded anymore. Finally you can call RefreshSection on the plugin section (or all plugins) to force the section to be reloaded with the changes. This gives you the ability to easily add new plugins and settings while still maintaining all settings in a single config file. If you really don't like the merge process (which can be done using System.Xml) then just use declarative programming to initialize a plugin's settings.
Michael Taylor - 3/9/09
Friday, June 04, 2010 6:02 PM
This can be done by writing your own custom settings classes and handlers. There are a lot of goo articles out there on how to do this. You start by creating the element class which inherits from ConfigurationElement (requires System.Configuration DLL Reference). Then you can create a configuration collection if you want to allow multiple settings of the same element type to be specified Inherited from CionfigurationCollection. From there you would create a Configuration Section handler that inherits from Configuration Section. This is where you get a hold of the configuration file and populate your classes. Then the magic happens....you use the configuration section class to read in those settings that exist in the config file (Much like the Settings class Microsoft creates).
It gets tricky if you dll also will be used in web however it can be done....you just have to determin if the request to read the settings is comming from Web (usually by trying to get a hold of the HTTP Context object and not web if null) or Windows. then you go after the proper config file.
Wednesday, April 11, 2012 7:17 PM
Event though this thread is pretty old I'll answer it for the ones who'll stumble upon it. I know I did that and cried when the questions matched oerfectly an issue I had but there was no answer. The config file for you specific situation has to be like this
<?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=18.104.22.168, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="Main_Namespace.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/> <section name="MyDLL_PlugIn.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=126.96.36.199, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/> </sectionGroup> </configSections> <applicationSettings> <!--Your main space settings--> <Main_Namespace.Properties.Settings> <setting name="UseSocketPlugin" serializeAs="String"> <value>True</value> </setting> <setting name="SocketIFacePlugIn" serializeAs="String"> <value>MPDEmulator.dll</value> </setting> </Main_Namespace.Properties.Settings> <!--Your library settings--> <MyDLL_PlugIn.Properties.Settings> <setting name="Random" serializeAs="String"> <value>True</value> </setting> </MyDLL_PlugIn.Properties.Settings> </applicationSettings> </configuration>
Note the two separate sections for each namespace and their registrations at the top. This way, when you use My.Settings in VB.Net for example you will get what you see in the config file and not the "fallback" default values hardcoded in the assembly as DefaultValueAttribute(s).
- Edited by Mircea Ion Wednesday, April 11, 2012 7:21 PM adding some important bits