Creating Custom Tasks
How to create a Custom Task using C#.
Frends fully supports creating your own Task packages. To do this you must create a .NET class library which is then wrapped in a NuGet package file and finally imported into Frends through the Tasks admin page.
Creating Custom Tasks is as easy as cloning or installing the .NET 8 Task template, which gives you a ready to go Visual Studio solution. Once done with development, the project can be packed to a NuGet package and imported to Frends. You can use your prefered C# IDE, such as Visual Studio, VS Code or Rider.
This guide will take you through steps and specifics on how to make a functional Frends Task.
Requirements
You will need to have .NET SDK installed, at minimum .NET SDK 8.0 is required.
Frends Tasks are usually written in C#, and for the best experience you will want a compatible integrated development environment (IDE). Some common examples are Visual Studio, VS Code and JetBrains Rider. You can also use any text editor and the dotnet command-line interface.
Some IDEs allow you to install the template to the project wizard but can always use dotnet new command when that is not possible.
Obtaining & Setting up the Template
Check out the instructions on how to get the template set up at .NET 8 Task template GitHub page.
Task Development Instructions
When creating a class library for a Frends Task, the Task should be implemented as a public static method with a return value. Non-static methods or methods with no return value (void) cannot be used as Tasks. The methods also cannot be overloaded, e.g. you cannot have Frends.TaskLibrary.CreateFile(string filePath) and Frends.TaskLibrary.CreateFile(string filePath, bool overwrite).
Task parameters
All parameters specified for the method will be used as Task parameters. If the parameter is of class type, it will be initialized as a structure.
For example:
using System.ComponentModel;
namespace Frends.TaskLibrary
{
/// <summary>
/// File action type (nothing/delete/rename/delete)
/// </summary>
public enum ActionType
{
/// <summary>
/// Nothing is done to the file
/// </summary>
Nothing,
/// <summary>
/// File will be deleted
/// </summary>
Delete,
/// <summary>
/// File will be renamed
/// </summary>
Rename,
/// <summary>
/// File will be moved
/// </summary>
Move
}
/// <summary>
/// File class
/// </summary>
public class File
{
/// <summary>
/// File path
/// </summary>
[DefaultValue("\"C:\\Temp\\myFile.json\"")]
public string Path { get; set; }
/// <summary>
/// Maximum size of the file
/// </summary>
[DefaultValue("0")]
public int MaxSize { get; set; }
/// <summary>
/// Password for unlocking the file
/// </summary>
[PasswordPropertyText]
public string Password { get; set; }
}
/// <summary>
/// FileAction class defines what will be done to the file
/// </summary>
public class FileAction
{
/// <summary>
/// Action to be done with the file
/// </summary>
public ActionType Action { get; set; }
/// <summary>
/// If ActionType is Move or Rename then To is the path to be used
/// </summary>
[DefaultValue("\"\"")]
public string To { get; set; }
}
public static class Files
{
/// <summary>
/// DoFileAction task does the desired action to file
/// </summary>
/// <param name="file">File to handle</param>
/// <param name="action">Action to perform</param>
/// <returns>Returns information if task was successful</returns>
public static string DoFileAction(File file, FileAction action)
{
// TODO: change logic
return $"Input values. Path: '{file.Path}', Max size: '{file.MaxSize}', Action: '{action.Action}', To: '{action.To}'";
}
}
}
In case of a complex or large parameter structure you can use custom attributes from ComponentModel library. Use assets in System.ComponentModel and System.ComponentModel.DataAnnotations namespaces to specify how the parameters are shown in the UI.
Default value: DefaultValueAttribute
Task parameters may use the DefaultValueAttribute
to provide a default value which is shown in the editor, remember that the parameters are expressions in the editor and default values need to be provided as such, e.g. "true" for a boolean value, "\"C:\Temp\\"" for a string containing a filePath.
Sensitive information not to be logged: PasswordPropertyTextAttribute
Also, if a parameter should not be logged, the PasswordPropertyTextAttribute
should be added. The value of the parameter will be replaced with << Secret >>
in the log. Parameters may have a more complex hierarchical structure, we recommend using at most only two levels of hierarchy.
Optional inputs: UIHintAttribute
[UIHint(nameof(Property),"", conditions: object[]]
Show or hide editor inputs based on the value of other inputs.
Example:
public bool Rename { get; set; }
[UIHint(nameof(Rename),"", true)]
public string NewFileName { get; set; }
The NewFileName field will only be visible if the Rename property has the value true.
public FileEnum FileOptions { get; set; }
[UIHint(nameof(FileOptions),"", FileEnum.Rename, FileEnum.CreateNew)]
public string NewFileName { get; set; }
The NewFileName field will only be visible if the FileOptions choise is either Rename or CreateNew
Default editor type: DisplayFormatAttribute
[DisplayFormat(DataFormatString = "")]
Sets the default editor input type. The parameter input editor will try to use this format when e.g. filling out new task parameters.
Possible values for DataFormatString
are:
Json
Text
Xml
Sql
Expression
Example:
[DisplayFormat(DataFormatString = "Sql")]
public string Query { get; set; }
Tabbed parameter panels: PropertyTabAttribute
[PropertyTab]
Group parameters as tabs
Example:
public static bool Delete([PropertyTab] string fileName, [PropertyTab] OptionsClass options)
Customize Task discovery
By adding a FrendsTaskMetadata.json file to the root of the NuGet package, unwanted static methods can be skipped by listing only methods which are wanted as Tasks. For example the following JSON structure would only cause the DoFileAction to be considered as a Task:
{
"Tasks": [
{
"TaskMethod": "Frends.TaskLibrary.FileActions.DoFileAction"
}
]
}
XML Documentation
Custom Tasks can also be commented/documented in the code by using XML Documentation comments. These comments will show up in the Process Task editor automatically if the documentation XML file is included inside the Task NuGet (if the nuget Id is Frends.TaskLibrary then a file Frends.TaskLibrary.xml will be searched).
Generation of this file can be done automatically by enabling Build/Ouput/ XML documentation from Visual Studio for example. When the comments are being queried, the Task parameter definition is checked first and if this is not found then the type definition will be checked.
Packaging Task as NuGet package
Task libraries are distributed as NuGet packages (.nupkg). The assembly name and package Id must be identical, e.g. Frends.TaskTemplate.dll and Frends.TaskTemplate.1.0.0.0.nupkg.
Usually a Task can be packed using command dotnet pack
, but sometimes when working with legacy code, you need to use nuget.exe and .nuspec files to pack the Task.
Debugging Tasks in Visual Studio
Frends Tasks are mostly open source, so you can check the source code of Tasks that you are using in Processes. Let's take a look at how you can debug issues in Tasks using Visual Studio. These instructions work for both Official Tasks and their source code, available at GitHub, as well as for your own Custom Tasks.
Getting the source code from GitHub
Let's say that you are having issues with the Frends.File.Read Task that you want to debug yourself. You can clone the source of the Task using Git. For example, you can clone the source code of the Frends.File package which contains the Frends.File.Read Task using the command below.
git clone https://github.com/FrendsPlatform/Frends.File.git
After cloning the source code, you can open the project from the file Frends.File.sln. Opening this file in Visual Studio will open the whole project.
Running unit tests
It is good practice to run Task unit tests before making any modifications or starting to debug an issue to make sure that the current tests are working. All Tasks' source code should contain unit tests which should work.
Some Tasks require a target system for unit tests to work, so you should check the README.md file for any instructions for running unit tests. Most of the Tasks don't require a target system for unit tests, so you should be able to run them straight away after opening the project in Visual Studio.
Back to our Frends.File.Read Task example, after opening the solution in Visual Studio, we should run unit tests to check if they are working. This can easily be done by right-clicking "Frends.File.Tests" in the Solution Explorer and selecting "Run Tests". This will build the solution and run unit tests.

After clicking "Run Tests", the Test Explorer window will open which will show the status of the unit test run. If all tests show green, then you are ready to start debugging your issue or implement changes to the Tasks.

Debugging the issue
The best way to figure out what could be causing an issue in the Task is to replicate the issue in unit tests. If you are able to create a test that would replicate the issue, then it is easier to pinpoint where exactly the issue may occur. After fixing the issue you can also verify that the issue is actually fixed using the same unit test that you used to replicate the issue.
The easiest way to create a new unit test to test some functionality is to copy an existing unit test and modify it to suit the case. If you are able to replicate the issue in Frends, then the same issue should occur in unit tests if you pass the same parameters to the Task method as you passed in Frends. If you cannot replicate the issue in unit tests then that would indicate that the issue is not in the Task itself, but maybe with the Agent that is running the Process or somewhere else outside the Task source code.
Fixing the issue
If you managed to replicate the issue in unit tests and you want to release a fix for the issue, there are three options.
The first option is to create a new issue in the Task package repository with details about the issue and how you managed to replicate it in Frends or in source code and then contact [email protected] with a short description of the issue and a link to the GitHub issue which has all the details.
The second option is to fork the repository to your own account, make the change to your own fork, create a Pull Request to the original repository and then contact [email protected]. These contribution instructions can be found from all repositories (for example Frends.File).
The third option is to create a Custom Task to which you copy the source code of the Task and then import that Custom Task to your Frends.
Last updated
Was this helpful?