# The activity file

> :::important
This document refers to a deprecated version of the UiPath Activity Creator for Visual Studio. Please see the new documentation [here](https://docs.uipath.com/marketplace/automation-cloud/latest/user-guide/how-to-create-activities).
:::

:::important
This document refers to a deprecated version of the UiPath Activity Creator for Visual Studio. Please see the new documentation [here](https://docs.uipath.com/marketplace/automation-cloud/latest/user-guide/how-to-create-activities).
:::

## Intro

Every activity begins with an Activity class file that defines its inputs, outputs, and execution logic. To better understand how this is done, open the **ChildActivity.cs** file in your Activity project and walk through it section by section. This is a simple activity that adds two numbers together and outputs their sum.

![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-36232-8a40cd55-1eb10e05.webp)

```
using System;
using System.Activities;
using System.Threading;
using System.Threading.Tasks;
using MyCompany.MyProduct.Activities.Properties;
using UiPath.Shared.Activities;
namespace MyCompany.MyProduct.Activities
{
    [LocalizedDisplayName(nameof(Resources.ChildActivityDisplayName))]
    [LocalizedDescription(nameof(Resources.ChildActivityDescription))]
    public class ChildActivity : AsyncTaskCodeActivity
    {
        #region Properties
        [LocalizedDisplayName(nameof(Resources.ChildActivityFirstNumberDisplayName))]
        [LocalizedDescription(nameof(Resources.ChildActivityFirstNumberDescription))]
        [LocalizedCategory(nameof(Resources.Input))]
        public InArgument<int> FirstNumber { get; set; }
        [LocalizedDisplayName(nameof(Resources.ChildActivitySecondNumberDisplayName))]
        [LocalizedDescription(nameof(Resources.ChildActivitySecondNumberDescription))]
        [LocalizedCategory(nameof(Resources.Input))]
        public InArgument<int> SecondNumber { get; set; }
        [LocalizedDisplayName(nameof(Resources.ChildActivitySumDisplayName))]
        [LocalizedDescription(nameof(Resources.ChildActivitySumDescription))]
        [LocalizedCategory(nameof(Resources.Output))]
        public OutArgument<string> Sum { get; set; }
        #endregion

        #region Constructors

        public ChildActivity()
        {
              Constraints.Add(ActivityConstraints.HasParentType<ChildActivity, ParentScope>(Resources.ValidationMessage));
        }

        #endregion

        #region Protected Methods
        protected override void CacheMetadata(CodeActivityMetadata metadata)
        {
              if (FirstNumber == null) metadata.AddValidationError(string.Format(Resources.MetadataValidationError, nameof(FirstNumber)));
              if (SecondNumber == null) metadata.AddValidationError(string.Format(Resources.MetadataValidationError, nameof(SecondNumber)));
              base.CacheMetadata(metadata);
        }
        protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
        {
              var property = context.DataContext.GetProperties()[ParentScope.ApplicationTag];
              var app = property.GetValue(context.DataContext) as Application;

              var firstValue = FirstNumber.Get(context);
              var secondValue = SecondNumber.Get(context);

              var sum = app.Sum(firstValue, secondValue);
              return ctx =>
              {
                  Sum.Set(ctx, sum);
              };
       }
        #endregion
    }
}
```

## Imports and namespace

The file starts with standard boilerplate. It imports some namespaces and then declares that this activity will itself live in the *MyCompany.MyProduct.Activities* namespace.

```
using System;
using System.Activities;
using System.Threading;
using System.Threading.Tasks;
using MyCompany.MyProduct.Activities.Properties;
using UiPath.Shared.Activities;
namespace MyCompany.MyProduct.Activities
{
```

## Class definition

All standard activity classes (such as the sample ChildActivity) extend `AsyncTaskCodeActivity`, which provides methods for design-time validation of properties and run-time execution logic, and can be found in the Shared folder included in your solution. Additionally, all activities that extend `AsyncTaskCodeActivity` may run asynchronously, allowing them to be used with the [Parallel](https://forum.uipath.com/t/how-to-use-parallel/4819/2) and [Parallel For Each](https://forum.uipath.com/t/parallel-for-each-loop/94551/8) activities in UiPath Studio.

```
[LocalizedDisplayName(nameof(Resources.ChildActivityDisplayName))]
[LocalizedDescription(nameof(Resources.ChildActivityDescription))]
public class ChildActivity : AsyncTaskCodeActivity
{
```

Above the ChildActivity class definition, you'll notice two attributes--`LocalizedDisplayName` and `LocalizedDescription`. These allow you to set the name and tooltip description of the activity that appear in UiPath Studio's activities pane. In this example, the attributes reference localized versions of each (e.g. `Resources.ChildActivityDisplayName`), but simple strings may be used as well. For more information on localization, [see here](https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/localization).

![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-34051-8126364a-ffbb63c0.webp)

![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-34191-a2f2a831-cbf0685a.webp)

## Properties

Next are the activity's properties, which appear in the Properties Pane of UiPath Studio. Each property has a display name and description as the top-level activity did, but also included is a Category attribute to help group related activities. Notice how `FirstNumber` and `SecondNumber` appear in the Input category, but `Sum` appears under Output.

```
[LocalizedDisplayName(nameof(Resources.ChildActivityFirstNumberDisplayName))]
[LocalizedDescription(nameof(Resources.ChildActivityFirstNumberDescription))]
[LocalizedCategory(nameof(Resources.Input))]
public InArgument<int> FirstNumber { get; set; }
[LocalizedDisplayName(nameof(Resources.ChildActivitySecondNumberDisplayName))]
[LocalizedDescription(nameof(Resources.ChildActivitySecondNumberDescription))]
[LocalizedCategory(nameof(Resources.Input))]
public InArgument<int> SecondNumber { get; set; }
[LocalizedDisplayName(nameof(Resources.ChildActivitySumDisplayName))]
[LocalizedDescription(nameof(Resources.ChildActivitySumDescription))]
[LocalizedCategory(nameof(Resources.Output))]
public OutArgument<int> Sum { get; set; }
```

![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-33787-a702edfb-fd1a709f.webp)

Properties may be declared as type `InArgument`, `InOutArgument`, or `OutArgument` and must indicate their expected type (e.g. string, int, ...).

## Constructor

```
public ChildActivity()
{
     FirstNumber = 10;
     Constraints.Add(ActivityConstraints.HasParentType<ChildActivity, ParentScope>(Resources.ValidationMessage));
}
```

The constructor is used to set default values and constraints on properties.

* In the example above, the `FirstNumber` property is given a default value of 10, which appears in the Properties Pane.

  ![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-35310-ad9b89d7-c54e6e7d.webp)
* Additionally, a constraint is placed on the activity itself, requiring that it be surrounded by a ParentScope. If it is not, a validation error is shown.

  ![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-33751-b43af125-c90794d5.webp)

## CacheMetadata

[CacheMetadata](https://docs.microsoft.com/en-us/dotnet/api/system.activities.codeactivity.cachemetadata) provides validation of an activity's properties, delegates, and/or child activities. In this example, we check that values have been provided for the two input properties, `FirstNumber` and `SecondNumber`, and throw a validation error if either is not provided. Until the validation errors are handled, a workflow will not run.

```
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
    if (FirstNumber == null) metadata.AddValidationError(string.Format(Resources.MetadataValidationError, nameof(FirstNumber)));
    if (SecondNumber == null) metadata.AddValidationError(string.Format(Resources.MetadataValidationError, nameof(SecondNumber)));
    base.CacheMetadata(metadata);
}
```

![docs image](https://dev-assets.cms.uipath.com/assets/images/marketplace/marketplace-docs-image-36093-d4f9e0db-a0f289b5.webp)

## ExecuteAsync

ExecuteAsync is where the execution logic of the activity is held.

* The first two lines of the method query the activity context--or the current state of the ChildActivity and the elements surrounding it--to retrieve an `Application` object from the ParentScope if one surrounds it. Note that, because of the constraint added in the constructor above, this activity will never be allowed to run if it is not already in a ParentScope.
* The next lines take the current values of the `FirstNumber` and `SecondNumber` properties and save them to local variables.
* The last lines perform a Sum operation on the inputted numbers and set the `Sum` property to this value. Because `Sum` is an output property, this value may then be used by subsequent activities in the workflow.
  ```
  protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
  {
      var property = context.DataContext.GetProperties()[ParentScope.ApplicationTag];
      var app = property.GetValue(context.DataContext) as Application;

      var firstValue = FirstNumber.Get(context);
      var secondValue = SecondNumber.Get(context);

      var sum = app.Sum(firstValue, secondValue);
      return ctx =>
      {
          Sum.Set(ctx, sum);
      };
  }
  ```

  :::important
  Notice that the ExecuteAsync method returns a `Task<Action<AsyncCodeActivityContext>>` object. A `Task` is returned so this activity can run asynchronously. An `Action` is returned within this Task in order to allow for complex functions to be executed after any asynchronous operations have completed. In the example above, this function simply sets the value of `Sum`. Lastly, the `Action` takes an `AsyncCodeActivityContext` parameter so the context from this activity may be used by any such functions running after the asynchronous operations have finished. If this was not the case, the context could be lost and the `Sum` would not be easily modifiable.
  :::
