Temporalio 0.1.0-alpha1
See the version list below for details.
dotnet add package Temporalio --version 0.1.0-alpha1
NuGet\Install-Package Temporalio -Version 0.1.0-alpha1
<PackageReference Include="Temporalio" Version="0.1.0-alpha1" />
paket add Temporalio --version 0.1.0-alpha1
#r "nuget: Temporalio, 0.1.0-alpha1"
// Install Temporalio as a Cake Addin #addin nuget:?package=Temporalio&version=0.1.0-alpha1&prerelease // Install Temporalio as a Cake Tool #tool nuget:?package=Temporalio&version=0.1.0-alpha1&prerelease
Temporal .NET SDK
Temporal is a distributed, scalable, durable, and highly available orchestration engine used to execute asynchronous, long-running business logic in a scalable and resilient way.
"Temporal .NET SDK" is the framework for authoring workflows and activities using .NET programming languages.
Also see:
- NuGet Package
- Application Development Guide (TODO: .NET docs)
- API Documentation
- Samples (TODO)
⚠️ UNDER ACTIVE DEVELOPMENT
This SDK is under active development and has not released a stable version yet. APIs may change in incompatible ways until the SDK is marked stable.
Notably missing from this SDK:
- Activity workers
- Workflow workers
(for the previous .NET SDK repo, see https://github.com/temporalio/experiment-dotnet)
Quick Start
Installation
Add the Temporalio
package from NuGet. For example, using the dotnet
CLI:
dotnet add package Temporalio --prerelease
NOTE: This README is for the current branch and not necessarily what's released on NuGet.
Usage
Workflow Definitions
The current SDK does not yet support workflows written in .NET, but workflows from other languages can still be defined in .NET to be properly typed.
Client
A client can be created an used to start a workflow. For example:
using Temporalio.Client;
// Create client connected to server at the given address and namespace
var client = await TemporalClient.ConnectAsync(new()
{
TargetHost = "localhost:7233",
Namespace = "my-namespace",
});
// Start a workflow
var handle = await client.StartWorkflowAsync(
IMyWorkflow.RunAsync,
"some workflow argument",
new() { ID = "my-workflow-id", TaskQueue = "my-workflow-queue" });
// Wait for a result
var result = await handle.GetResultAsync();
Console.WriteLine("Result: {0}", result);
Notes about the above code:
- Temporal clients are not explicitly disposable.
- To enable TLS, the
Tls
option can be set to a non-nullTlsOptions
instance. - Instead of
StartWorkflowAsync
+GetResultAsync
above, there is anExecuteWorkflowAsync
extension method that is clearer if the handle is not needed. - Non-typesafe forms of
StartWorkflowAsync
andExecuteWorkflowAsync
exist when there is no workflow definition or the workflow may take more than one argument or some other dynamic need. These simply take string workflow type names and an object array for arguments. - The
handle
above represents aWorkflowHandle
which has specific workflow operations on it. For existing workflows, handles can be obtained viaclient.GetWorkflowHandle
.
Data Conversion
Data converters are using to convert raw Temporal payloads to/from actual .NET types. A custom data converter can be set
via the DataConverter
option when creating a client. Data converters are a combination of payload converters, payload
codecs, and failure converters. Payload converters convert .NET values to/from serialized bytes. Payload codecs convert
bytes to bytes (e.g. for compression or encryption). Failure converters convert exceptions to/from serialized failures.
Data converters are in the Temporalio.Converters
namespace. The default data converter uses a default payload
converter, which supports the following types:
null
byte[]
Google.Protobuf.IMessage
instances- Anything that
System.Text.Json
supports
Custom converters can be created for all uses. Due to potential sandboxing use, payload converters must be specified as types not instances. For example, to create client with a data converter that converts all C# property names to camel case, you would:
using System.Text.Json;
using Temporalio.Client;
using Temporalio.Converters;
public class CamelCasePayloadConverter : DefaultPayloadConverter
{
public CamelCasePayloadConverter()
: base(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
{
}
}
var client = await TemporalClient.ConnectAsync(new()
{
TargetHost = "localhost:7233",
Namespace = "my-namespace",
DataConverter = DataConverter.Default with { PayloadConverterType = typeof(CamelCasePayloadConverter) },
});
Workflows
Workflows cannot yet be written in .NET but they can be defined.
Definition
Workflows are defined as classes or interfaces with a [Workflow]
attribute. The entry point method for a workflow has
the [WorkflowRun]
attribute. Methods for signals and queries have the [WorkflowSignal]
and [WorkflowQuery]
attributes respectively. Here is an example of a workflow definition:
using System.Threading.Tasks;
using Temporalio.Workflow;
public record GreetingInfo(string Salutation = "Hello", string Name = "<unknown>");
[Workflow]
public interface IGreetingWorkflow
{
static readonly IGreetingWorkflow Ref = Refs<IGreetingWorkflow>.Instance;
[WorkflowRun]
public Task<string> RunAsync(GreetingInfo initialInfo);
[WorkflowSignal]
public Task UpdateSalutation(string salutation);
[WorkflowSignal]
public Task CompleteWithGreeting();
[WorkflowQuery]
public string CurrentGreeting();
}
Notes about the above code:
- The workflow client needs the ability to reference these instance methods, but C# doesn't allow referencing instance
methods without an instance. Therefore we add a readonly
Ref
instance which is a proxy instance just for method references.- This is backed by a dynamic proxy generator but method invocations should never be made on it. It is only a for referencing methods.
- This is technically not needed. Any way that the method can be referenced for a client is acceptable.
- Source generators will provide an additional, alternative way to use workflows in a typed way in the future.
[Workflow]
attribute must be present on the workflow type.- The attribute can have a string argument for the workflow type name. Otherwise the name is defaulted to the
unqualified type name (with the
I
prefix removed if on an interface and has a capital letter following).
- The attribute can have a string argument for the workflow type name. Otherwise the name is defaulted to the
unqualified type name (with the
[WorkflowRun]
attribute must be present on one and only one public method.- The workflow run method must return a
Task
. - The workflow run method should accept a single parameter and return a single type that are often srecord. Records are encouraged because optional fields can be added without affecting backwards compatibility of the workflow definition.
- The parameters of this method and its return type are considered the parameters and return type of the workflow itself.
- This attribute is not inherited and this method must be explicitly defined on the declared workflow type. Even if the method functionality should be inherited, for clarity reasons it must still be explicitly defined even if it just invokes the base class method.
- The workflow run method must return a
[WorkflowSignal]
attribute may be present on any public method that handles signals.- Signal methods must return a
Task
. - The attribute can have a string argument for the signal name. Otherwise the name is defaulted to the unqualified
method name with
Async
trimmed off the end if it is present. - This attribute is not inherited and therefore must be explicitly set on any override.
- Signal methods must return a
[WorkflowQuery]
attribute may be present on any public method that handles queries.- Query methods must be non-
void
but cannot return aTask
(i.e. they cannot be async). - The attribute can have a string argument for the query name. Otherwise the name is defaulted to the unqualified method name.
- This attribute is not inherited and therefore must be explicitly set on any override.
- Query methods must be non-
Activities
Activities are not implemented yet.
Development
Build
With dotnet
installed with all needed frameworks and Rust installed (i.e.
cargo
on the PATH
), run:
dotnet build
Or for release:
dotnet build --configuration Release
Code formatting
This project uses StyleCop analyzers with some overrides in .editorconfig
. To format, run:
dotnet format
Can also run with --verify-no-changes
to ensure it is formatted.
VisualStudio Code
When developing in vscode, the following JSON settings will enable StyleCop analyzers in:
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableRoslynAnalyzers": true
Testing
Run:
dotnet test
Can add options like:
--logger "console;verbosity=detailed"
to show logs- TODO(cretz): This doesn't show Rust stdout. How do I do that?
--filter "FullyQualifiedName=Temporalio.Tests.Client.TemporalClientTests.ConnectAsync_Connection_Succeeds"
to run a specific test--blame-crash
to do a host process dump on crash
To help debug native pieces, this is also available as an in-proc test program. Run:
dotnet run --project tests/Temporalio.Tests
Extra args can be added after --
, e.g. -- --verbose
would show verbose logs and -- --help
would show other
options. If the arguments are anything but --help
, the current assembly is prepended to the args before sending to the
xUnit runner.
Rebuilding Rust extension and interop layer
To regen core interop from header, install ClangSharpPInvokeGenerator like:
dotnet tool install --global ClangSharpPInvokeGenerator
Then, run:
ClangSharpPInvokeGenerator @src/Temporalio/Bridge/GenerateInterop.rsp
The Rust DLL is built automatically when the project is built. protoc
may need to be on the PATH
to build the Rust
DLL.
Regenerating protos
Must have protoc
on the PATH
. Note, for now you must use protoc
3.x until
our GH action downloader is fixed or we change how we download
protoc and check protos (since protobuf
changed some C# source).
Then:
dotnet run --project src/Temporalio.Api.Generator
Regenerating API docs
Install docfx, then run:
docfx src/Temporalio.ApiDoc/docfx.json
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- Castle.Core (>= 5.1.0)
- Google.Protobuf (>= 3.21.9)
- Microsoft.Extensions.Logging.Abstractions (>= 2.2.0)
-
.NETFramework 4.6.2
- Castle.Core (>= 5.1.0)
- Google.Protobuf (>= 3.21.9)
- Microsoft.Extensions.Logging.Abstractions (>= 1.1.1)
- System.Text.Json (>= 6.0.7)
-
.NETStandard 2.0
- Castle.Core (>= 5.1.0)
- Google.Protobuf (>= 3.21.9)
- Microsoft.Extensions.Logging.Abstractions (>= 2.2.0)
- System.Text.Json (>= 6.0.7)
NuGet packages (8)
Showing the top 5 NuGet packages that depend on Temporalio:
Package | Downloads |
---|---|
Temporalio.Extensions.Hosting
Temporal SDK .NET Hosting and Dependency Injection Extension |
|
Temporalio.Extensions.OpenTelemetry
Temporal SDK .NET OpenTelemetry Extension |
|
Temporalio.Extensions.DiagnosticSource
Temporal SDK .NET Diagnostic Source Extension |
|
Neon.Temporal
Useful classes and extensions for Temporal |
|
OlegShilo.Temporalio.Graphs
Graph builder for Temporal Workflows |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.3.1 | 40,238 | 9/11/2024 |
1.3.0 | 30,417 | 8/14/2024 |
1.2.0 | 45,137 | 6/27/2024 |
1.1.2 | 19,748 | 6/5/2024 |
1.1.1 | 17,412 | 5/10/2024 |
1.1.0 | 2,459 | 5/7/2024 |
1.0.0 | 175,701 | 12/5/2023 |
0.1.0-beta2 | 27,135 | 10/30/2023 |
0.1.0-beta1 | 39,643 | 7/24/2023 |
0.1.0-alpha6 | 5,535 | 6/29/2023 |
0.1.0-alpha5 | 8,033 | 5/26/2023 |
0.1.0-alpha4 | 2,377 | 5/1/2023 |
0.1.0-alpha3 | 1,151 | 4/20/2023 |
0.1.0-alpha2 | 1,831 | 2/10/2023 |
0.1.0-alpha1 | 755 | 2/2/2023 |