CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 25
  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Question Accessing app config data

    Starting from the link that Arjay gave me in http://www.codeguru.com/forum/showthread.php?t=501684, I embarked on exploring the world of the .NET way of accessing application configuration data. And I must say it's a whole lot of stuff to read.

    In order to avoid getting lost and to keep it all under control, I decided to encapsulate the config stuff in a class while implementing it for one of my apps. Here's what I've got so far:

    Code:
    // Config.h
    
    #pragma once
    
    namespace Kalender {
    
    using namespace System;
    using namespace System::Configuration;
    
    public ref class Config
    {
    public:
      Config(void);
    
      Object ^GetItem(String ^Key);
      void SetItem(String ^Key, Object ^Value);
    
      property Object ^default[String ^]
      {
        Object ^get(String ^Key);
        void set(String ^Key, Object ^Value);
      }
    
    private:
      System::Configuration::Configuration ^m_Config;
      AppSettingsSection ^m_AppSettings;
    };
    
    }
    Code:
    // Config.cpp
    
    #include "StdAfx.h"
    
    #include "Config.h"
    
    using namespace Kalender;
    
    Config::Config(void)
    {
      m_Config = ConfigurationManager::OpenExeConfiguration(ConfigurationUserLevel::PerUserRoamingAndLocal);
    #ifdef _DEBUG
      Diagnostics::Debug::Write("Using configuration file: ");
      Diagnostics::Debug::WriteLine(m_Config->FilePath);
    #endif
      m_AppSettings = m_Config->AppSettings;
    #ifdef _DEBUG
      Diagnostics::Debug::Write("Using app settings file: ");
      Diagnostics::Debug::WriteLine(m_AppSettings->File);
    #endif
    }
    
    Object ^Config::GetItem(String ^Key)
    {
      return m_AppSettings->Settings[Key];
    }
    
    void Config::SetItem(String ^Key, Object ^Value)
    {
      m_AppSettings->Settings[Key] = Value;
    }
    
    Object ^Config::default::get(String ^Key)
    {
      return m_AppSettings->Settings[Key];
    }
    
    void Config::default::set(String ^Key, Object ^Value)
    {
      m_AppSettings->Settings[Key] = Value;
    }
    That went fine as long as I didn't implement the GetItem() method and the get indexer, although that wasn't of much use as there wasn't any configuration info to read yet. The GetItem() method and the get indexer, however, give me an error C3070, complaining about the fact that the AppSettingsSection::Settings property doesn't have a set accessor. This is no surprise, though, as it conforms to the MSDN docs.

    Trying to apply the indexer directly to the m_AppSettings object, OTOH, gives me the errors C3767 and C2661, complaining about that, as far as I understand it, the accessor exists but isn't accessible. And now this, IMO, stands in conflict with http://msdn.microsoft.com/en-us/library/c8693ks1.aspx (as inherited by the AppSettingsSection class).

    Any sugestions on how to proceed?

    TIA
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  2. #2
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    There's an easier way, for app settings use:
    Code:
    string appSetting = ConfigurationManager.AppSettings[ "appSettingsKey" ];
    For connection strings, use:
    Code:
    string connSetting = ConfigurationManager.ConnectionStrings[ "connKey" ];
    Not need to use OpenExeConfiguration at all (because web.config or app.config files get automatically loaded if they exist).

    Of course, the above is in C#.

  3. #3
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    Quote Originally Posted by Arjay View Post
    There's an easier way, for app settings use:
    Code:
    string appSetting = ConfigurationManager.AppSettings[ "appSettingsKey" ];
    This was one of my first ideas, but if I didn't get MSDN wrong, the settings I obtain that way are read-only, which is not what I want.

    And then there's this (from http://msdn.microsoft.com/en-us/libr...psettings.aspx):

    Quote Originally Posted by MSDN
    Returns a NameValueCollection object that contains the contents of the AppSettingsSection object for the current application's default configuration.
    Inherited default config might be ok when reading, but at least when writing back I want to access the user-specific config.

    Of course, the above is in C#.
    That's fine as long as I only have to translate it from C# to C++/CLI. I've got accustomed to that up to now. Translating it the other way 'round is another topic...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  4. #4
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    If you want to write, then use the Settings class or create your own xml file and use Xml Serialization. I wouldn't recommend trying to write to the web.config or app.config files (because they'll probably be locked down).

  5. #5
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    In the meantime I found out what caused my problems with the AppSettingsSection indexer: It is declared as protected public, what perfectly explains the errors I got. I overlooked that C++/CLI peculiarity in the first place. However, I don't really see the point in having protected members in a sealed class at all, but probably this is because the indexer is inherited from ConfigurationElement.

    Quote Originally Posted by Arjay View Post
    If you want to write, then use the Settings class [...].
    Exactly this is the path I'm pursuing now. I initially was hoping I could get away with an ultra-thin wrapper class around ConfigurationSection, which would only expose the indexer (which is protected public here as well), but I soon had to realize that it's by far not that simple.

    I'm now planning to implementing some kind of generic configuration class, similar to the one in the C# sample on the MSDN page on ConfigurationSection, but exposing an indexer (among other things of course) rather than semi-hardcoding the configuration properties like it is done in the sample.

    But what really isn't clear to me at the moment: Which members of ConfigurationSection are mandatory to be overridden in my derived class? Can you tell me or point me somewhere where I can find a clearer definition of that? I suppose at least the Properties property is really essential.

    TIA again. And I'm almost certain I'll be back with more questions about that until I'm done with it...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  6. #6
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    After a lot more digging in MSDN, I'm a big step further now. One of the most valuable things I discovered is http://www.codeproject.com/KB/dotnet...iguration.aspx, the first part of a 3-part article series about the .NET configuration infrastructure. It's really nice that the MSDN search feature doesn't only include what is literally MSDN.

    This is what I have so far, and unlike the last code I posted in this thread it actually does something useful:

    Code:
    // Config.h
    
    #pragma once
    
    namespace Kalender {
    
    using namespace System;
    using namespace System::Configuration;
    using namespace System::Windows::Forms;
    
    private ref class CustomConfigSection : public ConfigurationSection
    {
    public:
      CustomConfigSection();
    
      property Object ^default[String ^]
      {
        Object ^get(String ^Key);
        void set(String ^Key, Object ^Value);
      }
    
    protected:
      virtual property ConfigurationPropertyCollection ^Properties
      {
        ConfigurationPropertyCollection ^get() override
        {
          return m_Properties;
        }
      }
    
    private:
      ConfigurationPropertyCollection ^m_Properties;
    };
    
    public ref class Config
    {
    public:
      Config();
      ~Config();
    
      Object ^GetItem(String ^Key);
      void SetItem(String ^Key, Object ^Value);
    
      property Object ^default[String ^]
      {
        Object ^get(String ^Key);
        void set(String ^Key, Object ^Value);
      }
    
    private:
      System::Configuration::Configuration ^m_Config;
      CustomConfigSection ^m_Section;
    };
    
    }
    Code:
    // Config.cpp
    
    #include "StdAfx.h"
    
    #include "Config.h"
    
    using namespace Kalender;
    
    // CustomConfigSection members
    
    CustomConfigSection::CustomConfigSection() : m_Properties(gcnew ConfigurationPropertyCollection)
    {
      m_Properties->Add(gcnew ConfigurationProperty("windowLocation", Drawing::Point::typeid, Drawing::Point(-1, -1)));
    }
    
    Object ^CustomConfigSection::default::get(String ^Key)
    {
    #ifdef _DEBUG
      Diagnostics::Debug::Assert(m_Properties->Contains(Key), "Attempted to set an undefined configuration property");
    #endif
      return ConfigurationSection::default[Key];
    }
    
    void CustomConfigSection::default::set(String ^Key, Object ^Value)
    {
    #ifdef _DEBUG
      Diagnostics::Debug::Assert(m_Properties->Contains(Key), "Attempted to get an undefined configuration property");
    #endif
      ConfigurationSection::default[Key] = Value;
    }
    
    // Config members
    
    Config::Config(void)
    {
      m_Config = ConfigurationManager::OpenExeConfiguration(ConfigurationUserLevel::PerUserRoamingAndLocal);
    #ifdef _DEBUG
      Diagnostics::Debug::Write("Using configuration file: ");
      Diagnostics::Debug::WriteLine(m_Config->FilePath);
    #endif
      m_Section = static_cast<CustomConfigSection ^>(m_Config->Sections["calendarSettings"]);
      if (!m_Section) {
        m_Section = gcnew CustomConfigSection;
        m_Section->SectionInformation->AllowExeDefinition = ConfigurationAllowExeDefinition::MachineToLocalUser;
        m_Config->Sections->Add("calendarSettings", m_Section);
      }
    }
    
    Config::~Config()
    {
      try {
        m_Config->Save(ConfigurationSaveMode::Minimal);
      }
      catch (ConfigurationErrorsException ^e) {
        MessageBox::Show(String::Concat("Fehler beim Speichern der Konfigurationsdaten: ", e->Message), "Kalender",
          MessageBoxButtons::OK, MessageBoxIcon::Error);
      }
    }
    
    Object ^Config::GetItem(String ^Key)
    {
      return m_Section[Key];
    }
    
    void Config::SetItem(String ^Key, Object ^Value)
    {
      m_Section[Key] = Value;
    }
    
    Object ^Config::default::get(String ^Key)
    {
      return m_Section[Key];
    }
    
    void Config::default::set(String ^Key, Object ^Value)
    {
      m_Section[Key] = Value;
    }
    As I posted above, my original intention was to write a generic configuration class without the requirement to semi-hardcode the individual properties. But that's actually easier said than done... I could actually achieve that when writing the properties, but when it came to reading them back, the deserialization failed, throwing an exception with the complaint about an unknown property entry in the config file. So for now I bit the bullet and settled for semi-hardcoded properties.

    Offering only access to the properties in the Config class in the form of Object ^s via indexers taking a string key is a really narrow access path, but it's a sign that I've still not given up the idea of a generic config class.

    Apparently, the deserialization relies on the properties already being present in the property bag after construction of the custom configuration section class. That led me to the idea of storing the set of available properties as a property itself. In order to control the sequence of deserialization, I would probably have to use another configuration section class with a single hardcoded property for that sole purpose. Any thoughts about that concept?

    I will keep you informed about the progress of my config project, regardless of whether some more comments come in in the meantime... and regardless of whether anyone reads this or not...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  7. #7
    Join Date
    Apr 2004
    Location
    England, Europe
    Posts
    2,492

    Re: Accessing app config data

    Quote Originally Posted by Arjay View Post
    There's an easier way, for app settings use:
    Code:
    string appSetting = ConfigurationManager.AppSettings[ "appSettingsKey" ];
    I'd suggest defining an enum and using ToString in order to avoid annoying typo-bugs.
    Code:
    public enum class ResourceID
    {
        appSettingsKey
    };
    //...
    string appSetting = ConfigurationManager.AppSettings[ ResourceID::appSettingKey.ToString() ]; // Error appSettingKey not member of ResourceID
    My hobby projects:
    www.rclsoftware.org.uk

  8. #8
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    Agreed. I showed the hardcoded string as an example but typically wouldn't do that in production code.

    In fact I don't even make ConfigurationManager.AppSettings calls throughout my code.

    Instead I put this functionality into a singleton class where I can easily access the properties.

    Code:
    var timeout = MvConfig.Instance.CommandTimeout;
    Implementation (sorry for the C# code)

    Code:
    /// <summary>
    /// Mv configuration
    /// </summary>
    public class MvConfig : ConfigSettings
    {
        /// <summary>
        /// Singleton instance
        /// </summary>
        public static MvConfig Instance = new MvConfig( );
    
        /// <summary/>
        public string SecurityKey { get; private set; }
    
        /// <summary/>
        public string ImageDirectory { get; private set; }
    
        /// <summary>
        /// Sql CommandTimeout value (default: 30)
        /// <summary/>
        public int CommandTimeout { get; set; }
            
        /// <summary>
        /// Ctor - loads all settings
        /// </summary>
        private MvConfig( )
        {
            ImageDirectory = AppSetting( "ImageDirectory", "//Uploads Server/Sketch" );
    
            CommandTimeout = AppSetting( "CommandTimeout", 30 );
        }
    }
    The base class ConfigSettings does all the work.

    Code:
    /// <summary>
    /// Base class used to read project configuration settings
    /// </summary>
    public abstract class ConfigSettings
    {
        #region Protected Methods
    
        /// <summary>
        /// App setting
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="defaultValue">Default value</param>
        [SuppressMessage( "Microsoft.Design", "CA1031 DoNotCatchGeneralExceptionTypes" )]
        protected static bool AppSetting( string name, bool defaultValue )
        {
            try
            {
                return bool.Parse( ConfigurationManager.AppSettings [ name ] );
            }
            catch
            {
                return defaultValue;
            }
        }
    
        /// <summary>
        /// App setting
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="defaultValue">Default value</param>
        [SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes" )]
        protected static int AppSetting( string name, int defaultValue )
        {
            try
            {
                return int.Parse( ConfigurationManager.AppSettings [ name ], CultureInfo.InvariantCulture );
            }
            catch
            {
                return defaultValue;
            }
        }
    
        /// <summary>
        /// App setting
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="defaultValue">Default value</param>
        [SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes" )]
        protected static string AppSetting( string name, string defaultValue )
        {
            var value = ConfigurationManager.AppSettings [ name ];
             return String.IsNullOrEmpty( value ) ? defaultValue : value;
        }
    
        /// <summary>
        /// Connection setting
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="defaultValue">Default value</param>
        [SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes" )]
        protected static string ConnectionSetting( string name, string defaultValue )
        {
            var value = ConfigurationManager.ConnectionStrings [ name ].ConnectionString;
    
            return String.IsNullOrEmpty( value ) ? defaultValue : value;
        }
    
        #endregion Protected Methods
    }
    This implementation only reads the config values once at program startup. If you want it to pick up file changes on the fly, move the CfgManager calls out of the constructor and into each of the properties.

  9. #9
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    First, let me thank both of you for your contributions.

    Quote Originally Posted by Zaccheus View Post
    I'd suggest defining an enum and using ToString in order to avoid annoying typo-bugs.
    A really good idea. On first sight I thought I would have to override the enum's ToString() member in order to get a valid configuration property name out of it, but in fact of course I don't need to. I must have mixed that up in memory with the ToString() of some other type.

    I also thought that this approach wouldn't match my concept of the config class, but I think in fact it actually can adapted to it, at the worst case by adding another class layer.

    Quote Originally Posted by Arjay View Post
    In fact I don't even make ConfigurationManager.AppSettings calls throughout my code.

    Instead I put this functionality into a singleton class where I can easily access the properties.
    Aside from the fact that my config class is no singleton (at least not yet), I think the basic idea of your config class and mine is similar, and both are based on the approach shown in the MSDN docs.

    I have been away from this thread for some time because I had to struggle with another problem. It was in the same app that the config class is (originally) meant for, but unrelated to the config topic. I actually was on the verge of posting a question about that (in the Non-VC++ forum, as this was more a basic design issue than specific to C++/CLI), but finally got sorted that out on my own after some days.

    After that was done, I got back to the config thing, but couldn't really get the "generic config class" to work yet. One of the things I had to learn on the way was that configuration property names must not begin with "config" or "lock", as those are reserved. I didn't find that anywhere on MSDN, but found it out by detailed analysis of an exception that got thrown at me. Of course, this was easy to work around but it took some time to figure it out.

    As of now, I can't get the "meta config section" that contains the {property name, type, default value} tuples stored in the config file. I first thought that the config infrastructure would simply refuse to accept a configuration property of type ConfigurationPropertyCollection, but trying to encapsulate the individual properties in a Dictionary<String ^, ConfigurationProperty ^> didn't help either. Analysing the exception I get revealed the fact that no converter could be found for serializing the Dictionary (though the term "serializing" is not literally mentioned in the message).

    Unfortunately I didn't find anything on MSDN about which types can be serialized into config files and which can not. Before I start experimenting with custom converters I decided to ask whether anyone can point me to some more enlightening documentation.

    I also had a look at the MSDN docs on XML serialization (which I will later need for that app anyway, unrelated to the config stuff) but it wasn't no big help either yet.

    Any thoughts or pointers?

    I decided not to post the code I have now at the moment, because it's a bit messy after all that experiments and doesn't work either, but of course I can still post it if someone thinks it might be helpful for the discussion.

    TIA
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  10. #10
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    Show me a snippet of the xml you would like to have in the config file.

  11. #11
    Join Date
    Apr 2004
    Location
    England, Europe
    Posts
    2,492

    Re: Accessing app config data

    Quote Originally Posted by Eri523 View Post
    I must have mixed that up in memory with the ToString() of some other type.
    Many types just return their name.
    My hobby projects:
    www.rclsoftware.org.uk

  12. #12
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    Quote Originally Posted by Arjay View Post
    Show me a snippet of the xml you would like to have in the config file.
    Well, as I'm really no expert on XML, I did some experiments on XML serializatin in order to get (part of te) the XML you asked for. But that turned ot to be easier said than done. As the trouble I encountered during this process isn't really directly related to the topic of this thread, I have detached it into another thread. You would really do me a favor if you'd have a look on it, and even more if you would have some helpful comments.

    TIA
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  13. #13
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    I just need an example of how the xml will appear in the config file. Your other link doesn't show any xml.

  14. #14
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Accessing app config data

    Quote Originally Posted by Arjay View Post
    I just need an example of how the xml will appear in the config file.
    My problem with this is that I don't know enough about XML to create a reasonable mock-up off-hand. I simply don't know what any serialized collection would look like.

    Your other link doesn't show any xml.
    Right. It shows what I tried and how I failed to create it. I could post the code I experimented with, but I'm afraid it isn't really interesting. Anyway, I'll keep on trying if I get any more promising ideas.

    Sorry.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  15. #15
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Accessing app config data

    Here's one example of a collection in xml:

    Code:
    <employees>
      <employee nameFirst="John" />
      <employee nameFirst="Jim" />
    </employees>
    Can you represent the data now in xml?

    If not, post the class(es) with their public properties that you wish to serialize.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured