C# Script Execute

Executes a C# script.

Version 1

HTTP Request
POST /ado/v1/csharpscriptexecute

Header

ParameterDescription
Ocp-Apim-Subscription-KeyThe subscription key you received when you purchased a plan.

Request Body

Mandatory

ParameterTypeDescription
scriptStringThe C# script that will be executed.

Optional

ParameterTypeDescription
parametersObjectParameters used to pass into and be consumed by the script.
classDefinitionsString[]An array of class definitions that are able to be referenced from the script.

Notes

This operation has the ability to execute a fully formed C# script and return the resulting output as specified by a return statement.

Parameters

The parameters option can be used to pass in variables that are then able to be referenced within the script.

If there is a need to refer to a parameter in a class that has been defined in the classDefinitions option, it will need to be passed in to a constructor, assigned via a property or via a method when needing to be referenced.

Parameters are not global by nature, they are only directly accessible by the primary script.

Using the following example, a parameter called TestString has been defined with a value of This is a test.

"parameters": {
    "TestString": "This is a test"
}Code language: JavaScript (javascript)

Now within the script itself, it’s able to be referenced via the parameters variable that is supplied to the script in the background.

"Script": "return parameters.TestString;"Code language: JavaScript (javascript)

Logging

The main script block contains a private variable that is able to be used to capture log information as required. The private variable available is _logger and is able to be used anywhere within the main script block. If logging is required within custom class definitions, an instance of that variable will need to be passed to the class via the constructor or a method that is called within that class.

The standard methods for capturing log entries are:

_logger.LogInformation()
_logger.LogError()
_logger.LogCritical()
_logger.LogWarning()
_logger.LogDebug()
_logger.LogTrace()

When the execution of the script completes, an array containing each log entry (along with a prefix of the error level, i.e. I = Information, E = Error, C = Critical, W = Warning, D = Debug, T = Trace) will be returned as well as the value returned from the script itself.

More information of this implementation and the methods available can be found here:

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.logger-1

It is important to familiarise yourself with the possibilities and overloads of each method.

Restricted Namespaces

The functionality made available is not all encompassing. Invocations originating from volatile and security conscious namespaces have been restricted and are not able to be made use of through this operation.

Examples of restricted invocations:

  • HTTP/Web calls – There should be no need to make such calls given the framework you are calling this operation from can perform those tasks for you.
  • Reflection – This would allow the calling application to dive deeper into the underlying framework. It would also allow calling applications to invoke methods on external assemblies and therefore, is not permitted.
  • File System Operations – The file system is of no great use to the calling application and it would also open up the framework to potential vulnerabilities, for that reason, file system operations are not permitted. However, System.IO.* operations that deal with streams is permitted.
  • Process Invocation – Spawning applications is not permitted. This would open up the framework to vulnerabilities and is not seen as being something that is seen to be required within the scope of the operation.

If you require the use of a method within a restricted namespace that you believe should be permitted, please contact us through our support channel and we will consider the request. However, we make no guarantees that your request will be successful.

Additional External Assemblies

This is not permitted.

Instantiating objects that are referenced from external assemblies is not made possible by the framework.

Built-in Using Directives

You, as the developer, do not have the ability to add or remove a using directives. Those that exist are outside the scope of the core script that you as the developer are able to provide and those that have been provided for are considered to be of high importance and high usage. Those directives are:

using System;
using System.Linq;

using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

Given that be the case, any reference to a namespace or nested namespace that is not specified in the list above will need to be fully qualified.

Script Timeout

A script must have completed it’s execution within a 2 second period on the Basic pricing tier and within a 5 second period on the Standard pricing tier.

This limit does not include the time it takes to compile and validate the code for any restricted namespaces. It’s is merely the execution time that is monitored.

Examples

Hello World
Class Definitions
Parameters

This example is somewhat superfluous but, using the class Hello world example, shows how a script, with added complexity can be executed using the framework.

Take note how the creation of the CultureInfo instance is fully qualified.

Script

var hello = "hello";
var world = "WORLD";

var textInfo = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name, false).TextInfo;
return $"{textInfo.ToTitleCase(hello)} {world.ToLower()}";
Code language: JSON / JSON with Comments (json)

Request

{
    "Script": "var hello = \"hello\";\r\nvar world = \"WORLD\";\r\n\r\nvar textInfo = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.Name, false).TextInfo;\r\nreturn $\"{textInfo.ToTitleCase(hello)} {world.ToLower()}\";"
}
Code language: JSON / JSON with Comments (json)

Response

{
    "returnValue": "Hello world",
    "log": []
}
Code language: JSON / JSON with Comments (json)

Additional classes can be added to the request so as to provide more complete functionality that would typically exist in any well formed C# application.

The class definitions could be housed and retrieved from a GitHub repo or stored as free text in an online storage platform like Azure Storage or Amazon S3. From here, they could be retrieved and passed through into the class definition array and referenced from the supplied script.

It also demonstrates the logging capability.

Script

var people = new List<Person>()
{
    new Person() { Name = "John Smith", Age = 24, HeightInCentimetres = 186 },
    new Person() { Name = "Sarah Jones", Age = 28, HeightInCentimetres = 155 },
    new Person() { Name = "Luke Brown", Age = 22, HeightInCentimetres = 175 }
};

_logger.LogInformation($"{people.Count} people added to the list.");

return people.OrderBy(x => x.Age).ToList();
Code language: JSON / JSON with Comments (json)

Class Definitions

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int HeightInCentimetres { get; set; }
}
Code language: JSON / JSON with Comments (json)

Request

{
    "classDefinitions": [
        "public class Person\r\n{\r\n    public string Name { get; set; }\r\n    public int Age { get; set; }\r\n    public int HeightInCentimetres { get; set; }\r\n}"
    ],
    "Script": "var people = new List<Person>()\r\n{\r\n    new Person() { Name = \"John Smith\", Age = 24, HeightInCentimetres = 186 },\r\n    new Person() { Name = \"Sarah Jones\", Age = 28, HeightInCentimetres = 155 },\r\n    new Person() { Name = \"Luke Brown\", Age = 22, HeightInCentimetres = 175 }\r\n};\r\n\r\n_logger.LogInformation($\"{people.Count} people added to the list.\");\r\n\r\nreturn people.OrderBy(x => x.Age).ToList();"
}
Code language: JSON / JSON with Comments (json)

Response

{
    "returnValue": [
        {
            "Name": "Luke Brown",
            "Age": 22,
            "HeightInCentimetres": 175
        },
        {
            "Name": "John Smith",
            "Age": 24,
            "HeightInCentimetres": 186
        },
        {
            "Name": "Sarah Jones",
            "Age": 28,
            "HeightInCentimetres": 155
        }
    ],
    "log": [
        "I : 3 people added to the list."
    ]
}
Code language: JSON / JSON with Comments (json)

As previously described, parameters can be included in the request and subsequently be referenced within the script itself.

A parameter can also be specified as an array or an object.

Note: The for statement expects a long as the iterator as the framework will automatically cast the input parameter to a long, not an int.

Script

for (long i = parameters.LoopFrom; i < parameters.LoopTo; i++)
{
    _logger.LogInformation($"Loop {i}");
}

return $"{parameters.LoopTo - parameters.LoopFrom} loops were executed.";
Code language: JSON / JSON with Comments (json)

Request

{
    "parameters": {
        "LoopFrom": 3,
        "LoopTo": 9
    },
    "Script": "for (long i = parameters.LoopFrom; i < parameters.LoopTo; i++)\r\n{\r\n    _logger.LogInformation($\"Loop {i}\");\r\n}\r\n\r\nreturn $\"{parameters.LoopTo - parameters.LoopFrom} loops were executed.\";"
}
Code language: JSON / JSON with Comments (json)

Response

{
    "returnValue": "6 loops were executed.",
    "log": [
        "I : Loop 3",
        "I : Loop 4",
        "I : Loop 5",
        "I : Loop 6",
        "I : Loop 7",
        "I : Loop 8"
    ]
}
Code language: JSON / JSON with Comments (json)