Develop custom modules

The integration tool is built very modular and therefore allows to extend its functionality by writing custom modules. If your interested in details, you can read the C# Plugins & Modules-Tutorial. The integration tool is built very similar.

Before you start, I recommend you to download the sourcecode of the Integration Tool. In the Visual Studio-solution you can find the complete project we discuss here:

CustomStepVsLocation

All modules are built in a consistent way and have at least the following files:


├── References
| └── IntegrationTool.SDK
|
├── ConfigurationWindow.xaml
├── Icon.xml
├── ModuleName.cs
└── ModuleNameConfiguration.cs

There are certain things we need to make sure regarding the files above:

  • The IntegrationTool.SDK.dll must not be copied to the output-directory!
  • The Icon.xml must be an embedded resource!
  • The ConfigurationWindow.xaml must be a WPF-UserControl!

Module-Types

Where and how a module is used, can be defined by setting a module-type. Currently, there are five different types of them:

ConnectionsProvides a connection to a datasource. An example of it is i.e. the Sql-Connection.
StepExecutes one specific task. An example of it is the Execute Sql Command-flowstep. There are pretty much endless things a flowstep could do.
SourceEffectively loads the data from a data-source. I.e. from excel, mssql, xml etc. In ETL sources stand for the Extract-part.
TransformationsTransforms loaded data. I.e. string-manipulations, concatenation etc. In ETL this is the Transform-part.
TargetWrites data to its destination. So far there’s only dynamics-crm. In ETL this is the Load-part.

Getting started…

Before we actually begin, let me give you some insight about the modules and the module-loading mechanism.

On startup, the tool searches for modules in the assemblies lying within the following folders:

├── Modules\Steps
├── Modules\Connections
├── Modules\Sources
├── Modules\Transformers
└── Modules\Targets

Module recognition

To be found as module, a class must implement the interface IModule and it must have assigned a ModuleAttribute. We’ll cover both of them later in this tutorial!

The ModuleAttribute

Contains the metadata about the module. With these informations, the tool knows where to display the module, what modulename it should display, which icon it has, which configuration-class belongs to the module and so on. Each module-type has an own attribute type, as you can see on the following inheritance-classdiagram.

Module-Attributes inheritance

Building a step-module

This is the most simple type to build, so we start with it.

StepModuleAttribute

To be correctly registered in the IntegrationTool, the class implementing the stepmodule must be decorated with an attribute of type StepModuleAttribute. See the following example:

[StepModuleAttribute(
	// Defined once and not changed anymore!
	Name = "CustomStep",

	// Is shown in the editor
	DisplayName = "Custom Step",

	// Step, Source, Transformation, etc...
	ModuleType = ModuleType.Step,

	// Depending on the RequiresConnection-field, 
	// connections are shown in the configuration-window or not
	RequiresConnection = false,

	// Type which implements the configuration
	ConfigurationType = typeof(CustomStepConfiguration))]
public class CustomStep : IModule, IStep
{
}

IModule-interface

Next we need to implement the IModule interface, which provides the methods that gets called in the ProjectDesigner.

The SetConfiguration-method provides an instance of your configuration-class. You’ll take it and store it in your module as follows:

public class CustomStep : IModule, IStep
{
	public CustomStepConfiguration Configuration { get; set; }

	public void SetConfiguration(ConfigurationBase configurationBase)
	{
		this.Configuration = configurationBase as CustomStepConfiguration;
	}
}

The RenderConfigurationWindow-method is used to get the ConfigurationWindow UserControl. The configuration is passed to the ConfigurationWindow-UserControl, so that we can bind the configuration-values on the UserControl. In case of a step, the dataObject is null, as it doesn’t get any data passed!

When working in the ProjectDesigner, the returned usercontrol will be hosted automatically in a parent-window, which provides an infrastructure for your usercontrol like the connection-selection and create-, update- and cancel-buttons. As you can see, you do not have to implement the cancel-functionality yourself!

public class CustomStep : IModule, IStep
{
	public UserControl RenderConfigurationWindow(ConfigurationBase configurationBase, SDK.Database.IDatastore dataObject)
	{
		ConfigurationWindow configurationWindow = new ConfigurationWindow((CustomStepConfiguration)configurationBase);
		return configurationWindow;
	}
}

IStep-interface

The IStep-interface has an Execute-method. In it, we implement the action the module should do. While executing a package, the Integration Tool then calls this execute-method! Please note, that if the attribute-value RequiresConnection is set to false, the connection-parameter will be null!

public class CustomStep : IModule, IStep
{
	// IModule-Methods omited here

	public void Execute(IConnection connection, IDatabaseInterface databaseInterface, ReportProgressMethod reportProgress)
	{
		reportProgress(new SimpleProgressReport("Yeah, we run our code now!"));
 
		// Your execution code goes here
	}
}

The module configuration-class
The configuration-class is quite simple. There are only two requirements:

  • It must inherit from StepConfiguration
  • It must be serializable (but not marked explicitly as such)

See the following example of a moduleconfiguration-class

public class CustomStepConfiguration : StepConfiguration
{
	public string MyConfigValue { get; set; }
}

The next step is to design the configurationwindow and bind all the values of the CustomStepConfiguration-class.

Deploying the module
Finally we can build the module and put the created module-dll into the correct module-folder. That’s all!

If you have any questions, don’t hesitate to contact me! Do you want me to include your custom module into the Integration Tool? I’d be happy to do so!