Settings.Default.<property> always returns default value instead of value in persistant storage (XML file)

12,552

Solution 1

I'm addressing this exact issue in an application I'm in the midst of prototyping. Although Decker's suggestion of hacking the config files together should work I think this is a pretty inconvenient manual hack to perform as part of a build cycle. Instead of that I've decided that the cleanest solution is to just have each library parse its own library.dll.config file. Its still not perfect and it requires some extra boiler-plate code, but it seems to be the only way to get around the byzantine way that .Net handles these app.config files.

Solution 2

I use this technique all time time. Often I have a library assembly that requires certain settings, and I need them set both by testing projects as well as the primary "executable" assemblies -- be they web projects or Windows service projects.

You're correct in that when you create a settings file for any project, it adds an application config file. The value you enter for any setting is stored in two places -- the config file AND in attributes on the classes created by the settings infrastructure. When a config file is not found, the values embedded in the attributes are used.

Here is a snippet that shows such an attribute:

Here is a snippet that shows the default value of the ConcordanceServicesEndpointName in the generated class:

    [global::System.Configuration.ApplicationScopedSettingAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Configuration.DefaultSettingValueAttribute("InternalTCP")]

    public string ConcordanceServicesEndpointName {
        get {
            return ((string)(this["ConcordanceServicesEndpointName"]));
        }
    }

What you want to do is copy the configuration section out of the app.config file from the library assembly project and merge it (carefully) into the applicable web.config or app.config for the main assembly. At runtime, that's the only config file that is used.

Here is an example:

<configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="LitigationPortal.Documents.BLL.DocumentsBLLSettings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <LitigationPortal.Documents.BLL.DocumentsBLLSettings>
      <setting name="ConcordanceServicesEndpointName" serializeAs="String">
        <value>InternalTCP</value>
      </setting>
    </KayeScholer.LitigationPortal.Documents.BLL.DocumentsBLLSettings>
  </applicationSettings>

You should copy these sections into the "true" config file.

Solution 3

I have had this same problem for a long time - it's annoying.

I like the idea of making your own config file and having each DLL parse it, though it still might be easy to miss having to change the config.

One thing I have done in the past to at least make this a little easier is to make sure that any config values that the Setting1.Settings file are invalid.

For instance, I have a class that uses LINQ-To-SQL to talk to the DB. So it has a Setting1.settings file that it stores the connection string to database in. The default value that is entered (upon dragging and dropping the database tables into the designer) is the connection string of the dev database.

Once I have the DBML file created based off of the test database, I can go in and edit the Settings file and type in a database name like "FAKE_DATABASE".

That way, if you use the DLL in another project, and then forget to merge the config files to add in the proper config value for the DLL, at least you'll get an error saying something like "Cannot connect to FAKE_DATABASE".

Of course, if you have to work with the designer again, you'll have to change the value back to the value of your dev database.

Huge pain. They've gotta change this somehow.

Solution 4

Apparently your application is trying to read from the default config file (which is probably the application's config file). To make sure, add the key-value pair in the dll's config file to the application's config file, run the application and see if it is read this time.

Solution 5

I think I just found an explanation of why this isn't working for my DLL and my test application. Here is the concluding exception from some guy's blog:

The fix for this is to either make sure your application and the support assemblies have the same namespace or to make sure you merge the contents of AppName.exe.config and DllName.dll.config (yes when you compile a .dll now it generates this file, however it is ignored if you copy it to the application directory and is not automatically merged)

So either I have to keep the DLL and Application in the same namespace -or- I have to merge the contents of the DLL config file with the Application's config file.

(Doesn't this sort of defeat the purpose of the DLL? I thought a DLL was supposed to be an independent library.)

Perhaps this is why it works for my co-worker. The production application shares the same namespace as the DLL. (My test app clearly does not...)

UPDATE: I just sat down with my co-worker recently and talked about this problem again and it seems that it was never working for him either, but he hadn't realized it because he had set the initial value to be the same as the device we were trying to use. So of course it appeared to work at first, but as soon as we deployed it elsewhere with slightly different settings it was broken again.

Share:
12,552

Related videos on Youtube

jlemley
Author by

jlemley

Pretzel is a bit Twisted and sometimes a little Salty; He always goes great with beer! Quote of the Moment: Physicists stand on each others’ shoulders while computer scientists stand on each others' toes. (Sad, but often true.)

Updated on April 16, 2022

Comments

  • jlemley
    jlemley about 2 years

    I recently wrote a DLL in C# (.Net 2.0) which contains a class that requires an IP address. A co-worker of mine altered the class to retrieve the IP from a ".dll.config" (XML) file -- This apparently is automatically generated by the "Application Settings" file he created (Settings1.settings). The benefit of this was to allow the end-user to change the IP address in the XML/config file at will.

    Unfortunately, when I check his code out of the tree and try to compile (or use) this new code, any application calling this DLL only gets the default value, rather than the value from the file.

    The constructor that calls the config file looks like this:

        public class form : System.Windows.Forms.Form
        {
            public form()
            {
                // This call is required by the Windows Form Designer.
                InitializeComponent();
                IP = IPAddress.Parse(Settings1.Default.IPAddress);
            }
        }
    

    I found a reference to this problem on the MSDN forums where a user said:

    the 'old' values (the ones you define at development time) are hard coded. If the franework isn't able to access or open the config file it will use the defaults instead. This will always happen if you use settings in a dll.

    1. Does this mean that I cannot store an external value for a DLL in a config file? (My co-worker has somehow made this work...)

    2. Since my framework appears to be unable to access or open the config file, how do I figure out why it's failing? Or even detect when this happens?

    Decker: That helps a bit. Unfortunately, I am writing this DLL to a specification, so I don't actually have access to the Application's config file. As you'll note above, my co-worker created a "Settings1.settings" file. I didn't understand this at the time, but it seems now that adding the "1" keeps it out of the settings space of any application that calls it.

    I guess what I'm trying to figure out is why the DLL doesn't seem to find the config file sitting next to it in the same directory. Tracing thru the code step-by-step reveals nothing.

    As an aside, I can change the "Output Type" of my assembly from "Class Library" to "Windows Application" and add the following lines at the beginning of my DLL code:

        [STAThread]
        public static void Main(string[] args)
        {
            System.Windows.Forms.Application.Run(new form());
        }
    

    When I run this, it generates a different config file (a ".exe.config") and that one I can alter and have it pull the new data from the file. So I'm a bit confused. Any ideas?

  • jlemley
    jlemley over 15 years
    I added the same key-value pair from the DLL to the application's config file, but the application still reads the default value from the DLL (rather than the value contained in the DLL's .dll.config file.) :(
  • jlemley
    jlemley about 15 years
    I'm giving you the award here because that's what my team ended up doing.
  • Juan Calero
    Juan Calero over 14 years
    I think this is the correct answer and the best way to do it. For me, it makes sense that the settings affecting the application should be together in the same main config file rather than spread over N different config files. I don't think it's inconvenient as part of the build cycle. When you reference a dll and realize you need to be able to modify some settings on it, you add them to your app.config or web.config. And you are done. Just have to be careful with the configSections part when merging.
  • Steven Hunt
    Steven Hunt about 9 years
    As a note for future visitors to this question: check to see if you've recently changed your default namespace. The XML node itself matches to the namespace for your properties and therefore your settings. If you update your default namespace and don't update the XML, it will silently fall back to the default values in the .settings file instead of throwing an error.
  • Bitfiddler
    Bitfiddler over 6 years
    I'm not sure if you've actually tried this, but Settings.Default.someSetting is the only way to access 'someSetting' using the code generated by Settings file section in Project properties. If you try to access Settings.someSetting you will get a compile error (I tried since I have never seen this suggested before).