Saturday, October 31, 2009

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

This is Part 1 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]

So one of the great things I love about .NET is the configuration file. A more flexible file format than the old .ini file and not the registry (need I say more?). That being said, I’ve tended to rely on the simple AppSettings mechanism making it fairly easy to create and manage your settings in C# or VB.NET projects.

I love using them for things like default import file paths, remembering user interface settings, connection strings, URLs and such. Now I know there are other ways to store settings, such as in a database table, but sometimes the configuration file is the best tool for the job.

However, .NET DLLs, for a number of reasons I won’t get into here, don’t inherently have their own configuration files. They must access their parent application’s file. Although you can, in fact, use AppSettings, as long as you modify the host application’s (in this case Dynamics’) configuration file, there are some great reasons to go custom.  And it’s easier than you may think.

Since a Dynamics GP Add-In is a .NET DLL, the configuration file we have access to is the Dynamics.exe.config file. So how do we use it to store our own settings that can be used by our Add-Ins?

Step one in this process is to understand the structure and handling of configuration files.

Configuration files are broken into sections (there are also section groups but we’ll ignore those for this project). A section is simply an XML node off the top level configuration node. There’s really no restrictions on its structure as long as it’s valid XML. In GP 10 there is a single section, shell.

Each section must have a section definition that includes the assembly that contains the configuration handler object that reads that section. Let’s take a look at the Dynamics.exe.config file.

clip_image001

[Click here to download the default Dynamics.exe.config configuration file]

Notice that the first node in the configuration file is the configurationsections node. This is where sections are defined. Notice that here, Dynamics is defining the shell section, which you will note is the next node after the configuration sections node. The section definition consists of the name, which is the name of the top level node for that section, followed by the type, which defines the assembly and handler that reads that section.

So to add our own section to the configuration file, we simply need to add a section definition and then the corresponding section…oh yeah, and write a configuration section handler assembly.

In this example, we’re going to create a fairly. It will have a single node, GPAddIn and two properties, HelloConfigurationCaption for our message box, and ImportFileName for our hypothetical file integration. The properties are going to be implemented as attributes of the GPAddIn node, leaving us without the need to handle sub-nodes.

When all is said and done our modified Dynamics.exe.config file will look like this:

clip_image001[8]

[Click here to download the configuration file with the custom section]

Note that we’ve added a new section definition for the GPAddIn section and the section itself with our two settings as attributes. In the type attribute of the section definition, we have “GPAddInConfiguriation.GPAddInSectionHandler”, the actual class that reads the GPAddIn section, and “GPAddInConfiguration”, the name of the assembly itself.

So now that we can read the configuration file and define a custom section, how do we write the code to read it?

So a couple of basics. The section handler class itself is a class that derives from the System.Configuration.ConfigurationSection class. The actual assembly must be a .NET DLL and must be deployed to the same folder as the parent executable. Note that this means that it is in the Microsoft Dynamics\GP folder, not the AddIns folder that the add-in itself will be deployed to.

So let’s write some code! First things first. The source code for this solution can be downloaded from here.

We’re going to start with the configuration handler DLL project, GPAddInConfiguration. This project is a Class Library project and consists of a single C# class file containing a single class, GPAddInSectionHandler, derived from System.Configuration.ConfigurationSection. You will need to reference the System.Configuration assembly in the project.

The beginning of your class file will look as follows:

using System;
using System.Configuration;

namespace GPAddInConfiguration
{
public class GPAddInSectionHandler : ConfigurationSection
{

Next we’ll define the constructors:

// CustomSection constructor.
public GPAddInSectionHandler()
{
}

public GPAddInSectionHandler(string helloConfigurationCaption, string importFileName)
{
HelloConfigurationCaption = helloConfigurationCaption;
ImportFileName = importFileName;
}

After that we define the configuration attributes. We’re using the declarative method so we don’t need to worry about the configuration property collection or private variables:

[ConfigurationProperty("HelloConfigurationCaption", DefaultValue = "Hello Dynamics GP Configuration", IsRequired = true)]
[StringValidator(MinLength = 1, MaxLength = 60)]
public string HelloConfigurationCaption
{
get
{
return (string)this["HelloConfigurationCaption"];
}
set
{
this["HelloConfigurationCaption"] = value;
}
}

[ConfigurationProperty("ImportFileName", DefaultValue = "importfile.txt", IsRequired = true)]
[StringValidator(InvalidCharacters = " ~!@#$%^&*()[]{}/;'\"\\",
MinLength = 1, MaxLength = 255)]
public string ImportFileName
{
get
{
return (string)this["ImportFileName"];
}
set
{
this["ImportFileName"] = value;
}
}

Here we’ve defined the two configuration properties, HelloConfigurationCaption and ImportFileName. Using the ConfigurationProperty attribute we’ve defined their type, default value and whether or not their required without having to write any of the handling code.

Additionally, using the StringValidator attribute we can define what constitutes a valid value, such as maximum length, and in the case of the ImportFileName, invalid characters.

And that’s it. Configuration handler completed. Obviously there’s a lot more that you can do such as child elements and even complete custom handling of the section XML, but for a basic replacement of AppSettings, this works quite nicely, and I would argue, makes for a more readable configuration file.

Hopefully this has demystified the configuration file and will allow you to move beyond the simple AppSettings object. In Part 2, well take our section handler and run it through its paces in a test application.

No comments:

Post a Comment