How to develop Link 3 artifacts

This article describes how to develop artifacts to Link 3.

 

Prerequisites

  • Visual Studio 2022

  • .NET 6

Visual Studio Templates

It is possible to get project-templates in Visual Studio 2022 supporting all Link3 artifact-types.

To install the templates use the following command:

dotnet new install Bizbrains.Link.Templates --nuget-source https://bizbrains-productteam.pkgs.visualstudio.com/Link3Public/_packaging/Link3Public/nuget/v3/index.json

It’s recommended to close your running Visual Studio instances before installing the templates.

After installation you should be able to choose the “Link Step“ project type (search for Bizbrains):

image-20240730-142012.png

When creating the new project, you will be prompted to choose the type of artifact and to give your artifact a name (which will be the pre-fix for the auto-generated classes). Avoid spaces in the name.

image-20240730-142153.png

The templates contains a nuget.config file. If the nuget-dependencies can’t be resolved - move the nuget.config file to the same directory as your solution (.sln) file. Nuget.config is not supported on project-folder level.

Now you can compile the project and fill in the missing methods. Look for “TODO”'s in the code.

 

 

Nuget

Nuget-packages for Link 3 are placed in a public devops project here:

https://bizbrains-productteam.visualstudio.com/Link3Public/_artifacts/feed/Link3Public

Follow the link above to see the repository and click the “Connect to Feed” button to see setup instruction for numerous different tools.

Create a new nuget-source in visual studio using the this link: https://bizbrains-productteam.pkgs.visualstudio.com/Link3Public/_packaging/Link3Public/nuget/v3/index.json

It should look like this:

Notice! At the time of writing only pre-release packages are available, so it’s necessary to check the “Include prerelease” checkbox:

Otherwise no packages will be shown.

Bizbrains.Link.Base

This package contains all interfaces and base-classes needed to develop Link Artifacts.

Bizbrains.Link.Base.Implementations

This package contains some implementations from the base-package. Eg. an implementation of ILinkMessage, Exceptions, etc. It also contains the LinkPropertyAccessor implementation, which is used to access context properties in a strongly-types way.

Bizbrains.Link.Repositories

Contains all the repositories in Link. These repositories can be used to communicate with the database. A repository in Link is characterized by only handling a single entity. And an entity typically represents a table in the database.

Bizbrains.Link.Services

Services that contains business-logic are placed in this package.

Interfaces / base classes

The Bizbrains.Link.Base nuget package contains all needed interfaces used to develop artifacts for Link.
Below image is a class diagram for all base interfaces and abstract base classes.

Typically artifacts should be implemented using the abstract base classes and not the interfaces directly. This is because the base-classes provides some “convenience functionality” which makes it easier to implement an artifact for Link. The base classes also use generics to tell the type of the configuration-class for the artifact.

Flow base-classes

The following list is the base-classes needed when implementing what we call “flow-steps”. Flow-steps is implementations that can be hooked in different places in the flow (see Link 3 architecture - TODO: Make architecture drawing).

Retrieve Steps

Usage: (S)Ftp polling, Http polling, etc.
Base class: TransportRetrieveBase<T>

Initialize Steps

Usage: Pre-disassembler, disassembler, post-disassembler
Base class: InitializeStepBase<T>

Itinerary Steps

Usage: Transformation, validation, all kind of logic (previously implemented in Biztalk Pipelines)
Base class: ItineraryStepBase2<T>
Note: The original ItineraryStepBase<T> has been flagged as Obsolete and will be removed in the future, so it is important to use the new base class.

Send Steps

Usage: All kind of transport
Base class: TransportSendBase<T>

Outbox Steps

Usage: Used to place documents in outbox
Base class: TransportOutboxBase<T>

Duplicate Check Steps

Usage: Selects a specific value from the payload that the duplicate check framework should run against as part of the Initialize flow.
Base class: DuplicateCheckBase2<T>

Note: The original DuplicateCheckBase<T> has been flagged as Obsolete and will be removed in the future, so it is important to use the new base class.

Format Type Probe Steps

Usage: Validates that the payload format is of a specific type. E.g. XML
Base class: LinkFormatTypeProbeBase2<T>

Note: The original LinkFormatTypeProbeBase<T> has been flagged as Obsolete and will be removed in the future, so it is important to use the new base class.

HttpHandler Steps

Usage: Use this to expose any kind of functionality for an HTTP endpoint. This could be a custom API, a webhook, or even a website.
Base class: LinkHttpHandlerBase2<T>

Note: The original LinkHttpHandlerBase<T> has been flagged as Obsolete and will be removed in the future, so it is important to use the new base class.

Test Tool base-classes

The following list is the base-classes used for the test tool.

Compare Plugin

Usage: Have two responsibilities.
First is to prepare the stream for comparison. This could be based on the config of the plugin. Example is to handle ID’s or timestamps that are generated on run time.
Second is to compare the two streams.

Base class: TestToolCompareBase<T>

Tracking Action base-classes

The following is the base-class used for tracking actions.

Tracking UI Action

Usage: Have two responsibilities.
First is to validate the selected document or documents. Documents that are not in a final state will automatically be rejected, but the plugin can also reject documents based on its own requirements.
Second is to perform the actual action, which is also entirely up to the plugin.

The base class take two configuration classes. The first is for static configuration, that is configured when the button is configured; the second is runtime configuration, which the user will supply when activating the button.

Base class: TrackingActionBase<TConfig, TRunTimeConfig>

Dependency Injection

In Link 3 we use dependency injection. This means that we will always work on interfaces and not on the actual implementations. Therefore you will find that only interfaces, entities and models are public in our code.

If you are not familiar with dependency injection, it’s highly recommended that you get some basic knowledge before developing artifacts for Link 3.

You can find some information here: https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection

ILinkStep registration

All classes that implement (or derive from classes that implement) ILinkStep must be registered with DI as transient services. To ensure backwards compatibility, any such class that is registered as a scoped service is automatically (and silently) changed to a transient service. Any attempt to register an ILinkStep as a singleton results in an exception being thrown.

Singletons

While it is possible to register any service class, except ILinkStep’s, as a singleton, it is not supported and should be avoided because it will cause memory leaks and eventually containers will crash and restart.

Context properties

Note that only simple types in the value-field are supported (string, int, double, bool, etc.).

If complex types are added to the property-bag it will most likely throw an exception, when the message is serialized to the queue.

LinkPropertyAccessor

The LinkPropertyAccessor is used to access context-properties in a strongly-typed way. The constructor of the LinkPropeLinkPropertyAccessor takes an ILinkMessage or an ILinkPropertyBag, and then you can access all known properties in Link.This is a replacement for the xml-schemas used in Biztalk and Link 2.

The accessor provides methods for getting and setting values in context-properties without knowing the namespace and key for the property. It also provides methods for getting a value, which must exist - and throws a well-defined exception if the property doesn’t exist.

Example code:

var accessor = new LinkPropertyAccessor(linkMessage); // Optional use of property if (accessor.PartnerIn.IdentificationValue.HasValue) { var partnerInIdentification = accessor.PartnerIn.IdentificationValue.Value; // Do what's needed... } // Set context-property: accessor.PartnerIn.IdentificationValue.Value = "123456789"; accessor.PartnerIn.IdentificationQualifier.Value = "GLN"; // Mandatory use of property var partnerInIdentification = accessor.PartnerIn.IdentificationValue.ValueMandatory; // This will throw an exception if the distribution-id has not been set

See code examples to see more code.

Custom context properties

Via the ILinkPropertyBag it’s possible to add and maintain custom context-properties. There is no accessor for maintaining these properties, so you must cast the value yourself or use the generic-typed method.

Example code:

var propertyBag = linkMessage.Properties; // Set custom property propertyBag.Add("MyProperty", "http://mycompany/link/myproperty", "MyPropertyValue"); // or propertyBag["MyProperty", "http://mycompany/link/myproperty", false] = "MyPropertyValue"; // Get value var myValue = (string)propertyBag["MyProperty", "http://mycompany/link/myproperty"]; // or var myvalue = propertyBag.GetValue<string>("MyProperty", "http://mycompany/link/myproperty");

Readonly properties

The ILinkPropertyBag offers the possibility of setting readonly properties. This means that the property can only be set once - and after that it’s immutable across the entire flow.

Example code:

Artifact Configuration Classes

Artifacts can via configuration-classes expose properties that will be reflected in the Link UI.

So if your artifacts for example needs an URL which is required that the users types in, when configuring your artifact, you can simply create a property-class with an URL property inside.

Should I use LinkConfigBase or ILinkConfig?

The artifact configuration-framework contains two types of configurations:

  • Artifacts that support retries

    • Use the LinkConfigBase as your base class
      The areas that support retries are: Initialize, Itinerary and Send

  • Artifacts that doesn’t support retries

    • Use the ILinkConfig as your base class/interface
      The areas for this is Retrieve steps

Configuration attributes

There is a set of attributes, which can be used to control the apperance of the properties. See list below.

LinkDisplay

  • Name (The label of the presentation-field)

  • Description (The tooltip of the presentation-field)

  • Order (The order inside the group)

  • GroupName (The name of the presentation-group which this fields belongs to. If blank it will be set to “Settings”)

LinkStringLength

  • MaximumLength (The maximum length of the input-field)

LinkRequired

No properties

Mark the presentation-field as required

LinkRegularExpression

  • Pattern (The regular expression used on the input-field)

  • ErrorMessage (The message to show if the pattern doesn’t match the inputted value)

LinkDependsOn

This attribute can be used, if some properties depends on the value chosen in other properties. If the Value of the given PropertyName doesn’t match, the presentation-field will be hidden for the user - and all other validation for the field will be disabled.

Properties:

  • PropertyName (The name of the property this field is dependant of. Use the nameof keyword to get the name of the property instead of providing a string)

  • PropertyValues (The values that will make the field show in the UI)

LinkArray

LinkDefaultValue

LinkPassword

LinkArtifactAssembly

LinkArtifactXml

LinkEnum

LinkChoice

Code example

The following code shows different way of using the attributes:

The above will result in the following UI when used in a TransportSend<T> implementation:

The group “General” is generated because the configuration class inherits from LinkConfigBase, which have these properties.

LinkChoice attribute - populate dynamic values runtime

This attribute can be used to show a dropdown with dynamic values. Instead of using the enum-type, it’s here possible runtime to create a list of choices that can populated in a dropdown.

Examples of usage:

Simple static list of choices:

 

Use a service to provide choices:

 

Call an external service to provide choices:

 

LinkArray attribute

This attribute can be used to show a list of elements consisting of one or more fields. The user is allowed to add or remove elements. The number of elements can be restricted in code using the MinElements and MaxElements properties.

Examples of usage:

 

StepKeyName

All steps in Link inherits from the ILinkStep interface. And this interface has two properties:

  • ConfigType

  • StepKeyName

The ConfigType is the type of the configuration-object. This is typically implemented as a generic type in a derived base-class. Like: TransportRetrieveBase<T> where T will be set as the ConfigType via the base-class.

The StepKeyName is a string and must be a unique representation of the step. There are no validation of the name, but we highly recommend to use a standard way of naming your step.

For steps that are going via the MessageBroker (ItinerarySteps and SendTransportSteps) this name is used as the queue-name on the message-broker. Therefore it must be unique, so scaling can be done based on the steps.

Link comes with some system-artifacts, and the StepKeyName for these artifacts are prefixed with: “system-”. See https://bizbrains.atlassian.net/wiki/spaces/PT/pages/2408579160 for more information about this.

For custom steps we recommend the following convention (all in lower-case):

“customer-area-subarea-logic”

Customer:
The name of the company that has developed the step

Area:
This could be: “initialize”, “itinerary”, “transport”

Subarea:
This might not always be necessary, but for transport-types it could be: “retrieve”, “send”, “outbox”

Logic
This is a value that represents the logic for the step. Be so specific as possible to prevent collision with other steps. Examples: “xml-validation”, “xslt-transform”, “split-payload”, etc. (TODO: Find better examples).

Complete examples:

  • “bizbrains-initialize-xml-disassembler”

  • “microsoft-itinerary-edifact”

  • “apple-transport-send-to-icloud”

  • “amazon-transport-retrieve-from-webservices”

ILinkFactory<T> and LinkFactoryBase<T>

All artifacts in Link 3 have the responsibility for creating the instance of the ILinkStep they implement.

This is done via the LinkFactoryBase<T> base class which implement the ILinkFactory<T> interface.

Example of an LinkFactoryBase usage:

Logging

All logging in artifacts are done via the ILogger<T> interface, which is part of Microsoft.Extensions.Logging.

To get an ILogger<T> just put it in your constructor, and the dependency-injection will make sure you get an instance. Remember to use the type-specific ILogger<T> and not the generic ILogger interface.

Required packages: Microsoft.Extensions.Logging

Add serviceCollection.AddLogging(); in ConfigureServices(IServiceCollection serviceCollection) Method.

Cancellation token

Cancellation tokens provide a graceful way to signal to the underlying framework that it needs to stop what it is doing. This could, for example, be because the system is shutting down. This gives you control over how you want you code to stop, even if it hasn’t finished it’s task, which can be particularly useful in situations where your code could end up being long running.

Many of our interfaces have methods that take a cancellation token. By default cancellation tokens are checked immediately before the actual implementation of an artifact is executed.

Error handling

LinkExceptionBase

In Link 3 there is a base exception class, that should be used: LinkExceptionBase
By using this exception it’s possible to give an error-code, so the error will be routed to the right stakeholder in the error handling engine.

LinkFlowException

For steps running in an engine that supports retry (Itinerary and Send) - it’s possible to control the behavior of the retry-mechanism by throwing this exception.

LinkFlowException supports stopping the retry, if it doesn’t make sense to retry. Eg. validation - if the document fails in validation, it will also fail when retrying.

It’s also possible to give the document a specific status - eg. Ignored if this is needed:

Link Services and Repositories

In Link 3 there are different layers of services.

Repositories

The lowest layer is what we call repositories. A repository communicates with the database and takes or returns entities (like Partners, Distributions, etc.).

In Link 2 and below we provide nested entities via the “Common” layer. Eg. a Distribution contains full Partner objects - and the Partner objects contains full Identification objects - and so on.

This is changed in Link 3. An entity has only one level of properties - and no complex types are allowed on these entities.

This means that if you have a distribution-id and want to lookup details on one of the partners, you need to first load the DistributionEntity via the IDistributionRepository, then you need to load the PartnerEntity via the IPartnerRepository based on the partner-id on the distribution.

Services

Services are a higher level than repositories, and services typically provides some kind of business-logic.
Eg. we consider all itinerary-, send-, polling-, etc.- steps as services, because they each represents some business logic.

Link 3 also comes with some centralized helper-services. These services can be found in the Bizbrains.Link.Services package. (TODO: Create a list of typically used services).

 

As everything in Link 3 each repository and service is available through an interface and not the direct implementation.

Code examples

Retrieve Steps

The following example is a RetrieveStep that downloads the content of the configured Url and save it to Inbox.

Used base class: TransportRetrieveBase<T>
Required packages: Bizbrains.Link.Base, Bizbrains.Link.Base.Implementations, Bizbrains.Link.Services

Initialize Steps

The following code is an example of an artifact implementing the InitializeStepBase<T> base class. it is just a simple passthrough:

Used base class: InitializeStepBase<T>

Required packages: Bizbrains.Link.Base, Bizbrains.Link.Base.Implementations

Itinerary Steps

The following code is an example of an artifact implementing the ItineraryStepBase2<T> base class. The step can take an XPath and evaluate it against the payload, and promote the value to the configured property-namespace and property-name.

Used base class: ItineraryStepBase2<T>
Required packages: Bizbrains.Link.Base

 

The following example does not do anything with the payload. It’s just to show how to use repositories to lookup entities.

Used base class: ItineraryStepBase2<T>
Required packages: Bizbrains.Link.Base, Bizbrains.Link.Base.Implementations, Bizbrains.Link.Repositories

Send Steps

The following example shows how to implement a send-step. This step takes the body of the ILinkMessage and transmits it over Http. The configuration-class provides the Url and the Http Verb.

Used base class: TransportSendBase<T>
Required packages: Bizbrains.Link.Base

 

Duplicate Check Steps

The following example shows how to implement a duplicate check step. This step finds a specific value in the body of the ILinkMessage and returns it. The configuration-class provides the from and to position of the desired value in the paylod.

Used base class: DuplicateCheckBase2<T>
Required packages: Bizbrains.Link.Base

 

Format Type Probe

The following example shows how to implement a Format Type prober.

Used base class: LinkFormatTypeProbeBase2<T>
Required packages: Bizbrains.Link.Base, Bizbrains.Link.Base.Implementations

 

Compare Plugin

The following example shows how to implement a Compare Plugin.

Used base class: TestToolCompareBase<T>
Required packages: Bizbrains.Link.Base

Document Type Classifier Resolver Plugin

The following example shows how to implement a Document Type Classifier Resolver Plugin.

Used base class: DocumentTypeClassifierResolverBase2<T>
Required packages: Bizbrains.Link.Base

The information on this page is based on Link 3.00