|
1 | 1 | # Temporal .NET SDK |
2 | 2 |
|
3 | | -See: |
| 3 | +[](https://www.nuget.org/packages/Temporalio) |
| 4 | +[](LICENSE) |
4 | 5 |
|
5 | | -* NuGet Package (TODO) |
6 | | -* Application Development Guide (TODO) |
| 6 | +[Temporal](https://temporal.io/) is a distributed, scalable, durable, and highly available orchestration engine used to |
| 7 | +execute asynchronous, long-running business logic in a scalable and resilient way. |
| 8 | + |
| 9 | +"Temporal .NET SDK" is the framework for authoring workflows and activities using .NET programming languages. |
| 10 | + |
| 11 | +Also see: |
| 12 | + |
| 13 | +* [NuGet Package](https://www.nuget.org/packages/Temporalio) |
| 14 | +* [Application Development Guide](https://docs.temporal.io/application-development) (TODO: .NET docs) |
7 | 15 | * [API Documentation](https://dotnet.temporal.io/api) |
8 | 16 | * Samples (TODO) |
9 | 17 |
|
10 | 18 | ⚠️ UNDER ACTIVE DEVELOPMENT |
11 | 19 |
|
| 20 | +This SDK is under active development and has not released a stable version yet. APIs may change in incompatible ways |
| 21 | +until the SDK is marked stable. |
| 22 | + |
| 23 | +Notably missing from this SDK: |
| 24 | + |
| 25 | +* Activity workers |
| 26 | +* Workflow workers |
| 27 | + |
12 | 28 | (for the previous .NET SDK repo, see https://github.com/temporalio/experiment-dotnet) |
13 | 29 |
|
14 | 30 | ## Quick Start |
15 | 31 |
|
16 | | -TODO |
| 32 | +## Installation |
| 33 | + |
| 34 | +Add the `Temporalio` package from [NuGet](https://www.nuget.org/packages/Temporalio). For example, using the `dotnet` |
| 35 | +CLI: |
| 36 | + |
| 37 | + dotnet add package Temporalio --prerelease |
| 38 | + |
| 39 | +**NOTE: This README is for the current branch and not necessarily what's released on NuGet.** |
17 | 40 |
|
18 | 41 | ## Usage |
19 | 42 |
|
20 | | -TODO |
| 43 | +### Workflow Definitions |
| 44 | + |
| 45 | +The current SDK does not yet support workflows written in .NET, but workflows from other languages can still be defined |
| 46 | +in .NET to be properly typed. |
| 47 | + |
| 48 | +### Client |
| 49 | + |
| 50 | +A client can be created an used to start a workflow. For example: |
| 51 | + |
| 52 | +```csharp |
| 53 | +using Temporalio.Client; |
| 54 | + |
| 55 | +// Create client connected to server at the given address and namespace |
| 56 | +var client = await TemporalClient.ConnectAsync(new() |
| 57 | +{ |
| 58 | + TargetHost = "localhost:7233", |
| 59 | + Namespace = "my-namespace", |
| 60 | +}); |
| 61 | + |
| 62 | +// Start a workflow |
| 63 | +var handle = await client.StartWorkflowAsync( |
| 64 | + IMyWorkflow.RunAsync, |
| 65 | + "some workflow argument", |
| 66 | + new() { ID = "my-workflow-id", TaskQueue = "my-workflow-queue" }); |
| 67 | + |
| 68 | +// Wait for a result |
| 69 | +var result = await handle.GetResultAsync(); |
| 70 | +Console.WriteLine("Result: {0}", result); |
| 71 | +``` |
| 72 | + |
| 73 | +Notes about the above code: |
| 74 | + |
| 75 | +* Temporal clients are not explicitly disposable. |
| 76 | +* To enable TLS, the `Tls` option can be set to a non-null `TlsOptions` instance. |
| 77 | +* Instead of `StartWorkflowAsync` + `GetResultAsync` above, there is an `ExecuteWorkflowAsync` extension method that is |
| 78 | + clearer if the handle is not needed. |
| 79 | +* Non-typesafe forms of `StartWorkflowAsync` and `ExecuteWorkflowAsync` exist when there is no workflow definition or |
| 80 | + the workflow may take more than one argument or some other dynamic need. These simply take string workflow type names |
| 81 | + and an object array for arguments. |
| 82 | +* The `handle` above represents a `WorkflowHandle` which has specific workflow operations on it. For existing workflows, |
| 83 | + handles can be obtained via `client.GetWorkflowHandle`. |
| 84 | + |
| 85 | +### Data Conversion |
| 86 | + |
| 87 | +Data converters are using to convert raw Temporal payloads to/from actual .NET types. A custom data converter can be set |
| 88 | +via the `DataConverter` option when creating a client. Data converters are a combination of payload converters, payload |
| 89 | +codecs, and failure converters. Payload converters convert .NET values to/from serialized bytes. Payload codecs convert |
| 90 | +bytes to bytes (e.g. for compression or encryption). Failure converters convert exceptions to/from serialized failures. |
| 91 | + |
| 92 | +Data converters are in the `Temporalio.Converters` namespace. The default data converter uses a default payload |
| 93 | +converter, which supports the following types: |
| 94 | + |
| 95 | +* `null` |
| 96 | +* `byte[]` |
| 97 | +* `Google.Protobuf.IMessage` instances |
| 98 | +* Anything that `System.Text.Json` supports |
| 99 | + |
| 100 | +Custom converters can be created for all uses. Due to potential sandboxing use, payload converters must be specified as |
| 101 | +types not instances. For example, to create client with a data converter that converts all C# property names to camel |
| 102 | +case, you would: |
| 103 | + |
| 104 | +```csharp |
| 105 | +using System.Text.Json; |
| 106 | +using Temporalio.Client; |
| 107 | +using Temporalio.Converters; |
| 108 | + |
| 109 | +public class CamelCasePayloadConverter : DefaultPayloadConverter |
| 110 | +{ |
| 111 | + public CamelCasePayloadConverter() |
| 112 | + : base(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) |
| 113 | + { |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +var client = await TemporalClient.ConnectAsync(new() |
| 118 | +{ |
| 119 | + TargetHost = "localhost:7233", |
| 120 | + Namespace = "my-namespace", |
| 121 | + DataConverter = DataConverter.Default with { PayloadConverterType = typeof(CamelCasePayloadConverter) }, |
| 122 | +}); |
| 123 | +``` |
| 124 | + |
| 125 | +### Workflows |
| 126 | + |
| 127 | +Workflows cannot yet be written in .NET but they can be defined. |
| 128 | + |
| 129 | +#### Definition |
| 130 | + |
| 131 | +Workflows are defined as classes or interfaces with a `[Workflow]` attribute. The entry point method for a workflow has |
| 132 | +the `[WorkflowRun]` attribute. Methods for signals and queries have the `[WorkflowSignal]` and `[WorkflowQuery]` |
| 133 | +attributes respectively. Here is an example of a workflow definition: |
| 134 | + |
| 135 | +```csharp |
| 136 | +using System.Threading.Tasks; |
| 137 | +using Temporalio.Workflow; |
| 138 | + |
| 139 | +public record GreetingInfo(string Salutation = "Hello", string Name = "<unknown>"); |
| 140 | + |
| 141 | +[Workflow] |
| 142 | +public interface IGreetingWorkflow |
| 143 | +{ |
| 144 | + static readonly IGreetingWorkflow Ref = Refs<IGreetingWorkflow>.Instance; |
| 145 | + |
| 146 | + [WorkflowRun] |
| 147 | + public Task<string> RunAsync(GreetingInfo initialInfo); |
| 148 | + |
| 149 | + [WorkflowSignal] |
| 150 | + public Task UpdateSalutation(string salutation); |
| 151 | + |
| 152 | + [WorkflowSignal] |
| 153 | + public Task CompleteWithGreeting(); |
| 154 | + |
| 155 | + [WorkflowQuery] |
| 156 | + public string CurrentGreeting(); |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +Notes about the above code: |
| 161 | + |
| 162 | +* The workflow client needs the ability to reference these instance methods, but C# doesn't allow referencing instance |
| 163 | + methods without an instance. Therefore we add a readonly `Ref` instance which is a proxy instance just for method |
| 164 | + references. |
| 165 | + * This is backed by a dynamic proxy generator but method invocations should never be made on it. It is only a for |
| 166 | + referencing methods. |
| 167 | + * This is technically not needed. Any way that the method can be referenced for a client is acceptable. |
| 168 | + * Source generators will provide an additional, alternative way to use workflows in a typed way in the future. |
| 169 | +* `[Workflow]` attribute must be present on the workflow type. |
| 170 | + * The attribute can have a string argument for the workflow type name. Otherwise the name is defaulted to the |
| 171 | + unqualified type name (with the `I` prefix removed if on an interface and has a capital letter following). |
| 172 | +* `[WorkflowRun]` attribute must be present on one and only one public method. |
| 173 | + * The workflow run method must return a `Task`. |
| 174 | + * The workflow run method _should_ accept a single parameter and return a single type that are often srecord. Records |
| 175 | + are encouraged because optional fields can be added without affecting backwards compatibility of the workflow |
| 176 | + definition. |
| 177 | + * The parameters of this method and its return type are considered the parameters and return type of the workflow |
| 178 | + itself. |
| 179 | + * This attribute is not inherited and this method must be explicitly defined on the declared workflow type. Even if |
| 180 | + the method functionality should be inherited, for clarity reasons it must still be explicitly defined even if it |
| 181 | + just invokes the base class method. |
| 182 | +* `[WorkflowSignal]` attribute may be present on any public method that handles signals. |
| 183 | + * Signal methods must return a `Task`. |
| 184 | + * The attribute can have a string argument for the signal name. Otherwise the name is defaulted to the unqualified |
| 185 | + method name with `Async` trimmed off the end if it is present. |
| 186 | + * This attribute is not inherited and therefore must be explicitly set on any override. |
| 187 | +* `[WorkflowQuery]` attribute may be present on any public method that handles queries. |
| 188 | + * Query methods must be non-`void` but cannot return a `Task` (i.e. they cannot be async). |
| 189 | + * The attribute can have a string argument for the query name. Otherwise the name is defaulted to the unqualified |
| 190 | + method name. |
| 191 | + * This attribute is not inherited and therefore must be explicitly set on any override. |
| 192 | + |
| 193 | +### Activities |
| 194 | + |
| 195 | +Activities are not implemented yet. |
21 | 196 |
|
22 | 197 | ## Development |
23 | 198 |
|
@@ -101,16 +276,3 @@ Then: |
101 | 276 | Install [docfx](https://dotnet.github.io/docfx/), then run: |
102 | 277 |
|
103 | 278 | docfx src/Temporalio.ApiDoc/docfx.json |
104 | | - |
105 | | -TODO: |
106 | | - |
107 | | -* Fix generated api doc |
108 | | - * Specifically make `Temporalio.Api` have children collapsed by default |
109 | | - * Switch/update template to full width |
110 | | -* Formatting/style guide: |
111 | | - * Line len 100 max on everything where reasonable |
112 | | - * Rules for options classes |
113 | | - * Shallow-copyable via virtual clone |
114 | | - * Empty constructor and constructor with required params |
115 | | - * TODO(cretz): Validation? Probably don't want attributes? |
116 | | -* Source generator for workflows |
|
0 commit comments