Thursday, November 19, 2009

eConnect Message Queue vs. COM+ vs. Stored Procedures

A few months ago I wrote about how, for various reasons, I was fed up with the Dynamics GP Web Services and was moving towards writing my own web services working against eConnect. (Click here for that article)

Now the reality is that most of the integrations I write are small Windows Forms or intranet applications run within the local LAN and can directly access the SQL server.  I also wondered about writing GP Add-Ins using the Visual Studio Tools for Dynamics GP. 

In either of these scenarios, not only do I not see the sense of using the Dynamics GP Web Services, but of even using the two most documented methods of accessing eConnect using the COM+ or Message Queue interfaces.  I’ve started looking at using the base stored procedures directly in the database. 

Since each XML node maps one to one to a stored procedure of the same name, I’m able to just use the schema documentation to see what the various parameters mean.  I haven’t put anything into production yet with this, but I’ll let you know how it goes.

Anyone else use eConnect this way?  Let me know.

Sunday, November 1, 2009

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

This is Part 3 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 finally, in this part, we’ll put it all together and demonstrate how to access the Dynamics.exe.config configuration from a Visual Studio Tools for Dynamics GP add-in.  For this, we’ll create a simple “Hello Configuration” add-in.  We’ll attach it to the Additional menu of the SOP Batch Entry form as we might a SOP integration.

There’s really only two parts of this add-in:

  • Initialization where we verify the existence of the configuration section and add it if it’s missing and add a menu item and associated event handler to the Additional menu of the SOP Batch Entry form.
  • Event handler, that in this case simply displays the “Hello Configuration” caption, as stored in the configuration section, and the hypothetical import file name from the same section.

Thus the code consists of a single class file in the HelloConfigurationAddIn project of the solution linked to above.  The project was created using the Microsoft Dynamics GP Add-In template.  A reference is added to the GPAddInConfiguration project.  The entire code for the add-in is as follows:

using System.Configuration;
using GPAddInConfiguration;

namespace HelloConfigurationAddIn
{
    public class GPAddIn : IDexterityAddIn
    {
        public void Initialize()
        {
            // Check to see if the section exists and if not create it.
            this.VerifyConfigurationSection();

            // Add to Additional menu for SOP batch form.
            Dynamics.Forms.SopBatchEntry.AddMenuHandler(HelloConfigurationEvent,"Hello Configuration");
        }

        //  Verifies the existence of the section and adds it if missing.
        void VerifyConfigurationSection()
        {
            // If section doesn't exist create it.
            if (ConfigurationManager.GetSection("GPAddIn") == null)
            {
                // Get the current configuration file for writing.
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

                // Create the new section.
                GPAddInSectionHandler section = new GPAddInSectionHandler();
                config.Sections.Add("GPAddIn", section);
                section.SectionInformation.ForceSave = true;

                // Save the section
                config.Save(ConfigurationSaveMode.Full);

                ConfigurationManager.RefreshSection("GPAddIn");
            }
        }

        // Script to handle menu entry callbacks
        void HelloConfigurationEvent(object sender, EventArgs e)
        {
            GPAddInSectionHandler section = (GPAddInSectionHandler)ConfigurationManager.GetSection("GPAddIn");
            MessageBox.Show(section.HelloConfigurationCaption + "\n\nImport File:  " + section.ImportFileName, "Hello Configuration");
        }
    }
}

The Initialize method calls the VerifyConfigurationSection function that checks for the existence of the section in the Dynamics.exe.config configuration, and if missing, adds it with the default values.  Additionally, it adds a menu handler for the Additional menu of the SOP Batch Entry form, passing the HelloConfigurationEvent function as the event handler.

The HelloConfigurationEvent simply opens the GPAddIn section of the configuration and displays the HelloConfigurationCaption and ImportFileName values in a message box.

To properly deploy the add-in you need to copy the HelloConfigurationAddIn assembly DLL to the GP AddIns folder as you would any add-in.  However, you also need to deploy the GPAddInConfiguration assembly to the GP folder that contains the Dynamics.exe.config file.  Configuration handler assemblies must reside in the same folder as the host application.

So give it a whirl and see how changes to the values in the configuration file show up in the message box.  Note that in this case, changes don’t show up until you restart the application.

I hope these posts have inspired you to make use of the power of the configuration file in your Dynamics GP add-ins.  There’s certainly much more you can do once you dig into them.  Let me know what you find.

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.