Sunday, November 1, 2009

Accessing the Dynamics GP Configuration File (Dynamics.exe.config) from a Visual Studio Tools for Dynamics GP Add-In – Part 2

This is Part 2 of a three part article on working with the Dynamics.exe.config .NET configuration file.  with the following articles:

[Click here for the Visual Studio solution used in these articles]

Now that we’ve created a configuration section handler, let’s create a little console application to test it out and see some of the things we can do with it.  Note that we’re working with the section handler independently of Dynamics GP.  Section handlers are not tied to a particular applications configuration file, but can be referenced in any application.

In this test we’ll do the following:

  • Attempt to get the section, and if it doesn’t exist create it.
  • Attempt to update the configuration settings on a section object retrieved using the ConfigurationManager objects GetSection object.
  • Show how to update and save changes to the section object.

This application is the ConfigurationSectionTester project in the solution.  It is a simple console application and references the System.configuration and the GPAddInConfiguration project assembly.  Note that the configuration assembly must be deployed in the same folder as the calling application and it’s corresponding configuration file.

I’ve created static functions to perform each of the tasks listed above and call them from the Main function so that it looks as follows:

static void Main(string[] args)
{
    verifyAndGetSection();
    attemptUpdateOfReadonlySection();
    updateSection();

    Console.WriteLine();
    Console.WriteLine("Hit Enter to continue...");
    Console.ReadLine();
}

So let’s look at each of the functions individually.  First the verifyAndGetSection function.

private static void verifyAndGetSection()
{
    GPAddInSectionHandler section;

    Console.WriteLine();
    Console.WriteLine("----------");
    Console.WriteLine("Getting or creating section");

    // Attempt to get section from the static ConfigurationManager object.
    section = (GPAddInSectionHandler)ConfigurationManager.GetSection("GPAddIn");

    // If config section doesn't exist, create the section entry
    // in <configSections> and the
    // related target section in <configuration>.
    if (section == null)
    {
        // Open current configuration file for writing.

        Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        section = new GPAddInSectionHandler();
        config.Sections.Add("GPAddIn", section);
        section.SectionInformation.ForceSave = true;
        config.Save(ConfigurationSaveMode.Full);
        ConfigurationManager.RefreshSection("GPAddIn");

        Console.WriteLine("New section, {0}, created", section.SectionInformation.Name);
    }
    else
    {
        Console.WriteLine("Section {0} opened.", section.SectionInformation.Name);
    }

    Console.WriteLine();

    Console.WriteLine("Hello Configuration Caption - {0}", section.HelloConfigurationCaption);
    Console.WriteLine("Import File Name - {0}", section.ImportFileName);
}

In this function, we first attempt to retrieve the custom section by calling the ConfigurationManager object’s static GetSection method, passing it the section’s name.  If successful, this returns a read-only copy of the section.  If unsuccessful, it returns null (or Nothing in VB.NET).

If the returned section is null then we open a writeable copy of the configuration using the static OpenExeConfiguration method of the ConfigurationManager object.  Note that the parameter passed to this method indicates whether we’re getting the application or user specific configuration file.

Once we have the configuration object, we can add the new section to the Sections collection, mark it to force a save, and then save the configuration.  Note that all of this code works even if a configuration file doesn’t even exist.  Saving the configuration will create a new configuration file if one doesn’t already exist.

Calling the RefreshSection method forces the new section to be loaded when it is next requested within the application.  Otherwise attempting to load the section using the GetSection method prior to restarting the application will return a null object.

In the second function, attemptUpdateOfReadonlySection, we’ll show what happens if you attempt to update a read-only section object.

private static void attemptUpdateOfReadonlySection()
{
    Console.WriteLine();
    Console.WriteLine("----------");
    Console.WriteLine("Attempting to update a readonly section.");

    // Getting a section using GetSection returns a read-only section.
    GPAddInSectionHandler section = (GPAddInSectionHandler)ConfigurationManager.GetSection("GPAddIn");

    try
    {
        section.ImportFileName = "NewFileName.txt";
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error setting section attribute - {0}", ex.Message);
    }
}

Here, we again attempt to retrieve the section using the ConfigurationManager objects static GetSection method.  GetSection returns a read-only copy of the section.  If you attempt to set any of the properties it will throw an error.  Note that this is true whether or not you intend to ultimately save the update to disk.

In the final function, updateSection, we show how to successfully update a sections properties and then save it to disk.  This is very similar to how we created a new section in the verifyAndGetSection function.

private static void updateSection()
{
    // To get a writeable section you open the configuration using OpenExeConfiguration
    // and the return the section from the Sections collection.
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    GPAddInSectionHandler section = (GPAddInSectionHandler)config.Sections["GPAddIn"];

    Console.WriteLine();
    Console.WriteLine("----------");
    Console.WriteLine("Updating a section.");

    section.HelloConfigurationCaption = section.HelloConfigurationCaption + " - Modified";
    section.ImportFileName = "NewFileName.txt";

    // Set ForceSave to true to ensure the section is saved even if not updated
    section.SectionInformation.ForceSave = true;
    config.Save(ConfigurationSaveMode.Full);

    Console.WriteLine();

    Console.WriteLine("Hello Configuration Caption - {0}", section.HelloConfigurationCaption);
    Console.WriteLine("Import File Name - {0}", section.ImportFileName);
}

Here we open the configuration using the static OpenExeConfiguration method of the ConfigurationManager object, again, specifying whether we want the application or user specific version of the configuration.  We then retrieve the section from the Sections collection rather than using the GetSection method.  This gives us a writeable version of the section.  Once we’ve updated the section we then use the Save method of its parent configuration to save it to disk.

When testing this, run it from the command line rather than from the Visual Studio IDE.  When running it from the IDE, the initial state of the configuration file (i.e. no file), is restored each time.

The first time you run this, make sure you should start with no configuration file and your command window should look as follows:

clip_image001[7]

Note that first function is creating a new section and setting the values to their defaults.  When you run it a second time you should see the following:

clip_image001[5]

Here you’ll note that the section is being opened and showing the section values updated from the defaults in the previous run.  Also note the “The configuration is read only” error message when attempting to update the section returned by the GetSection method.

Hopefully this has given you some of the basic ways of using successfully using your configuration handler.  In the Part 3, we’ll tie it all together in a Dynamics GP Add-In.

No comments:

Post a Comment