Skip to main content

C# SDK

The C# SDK for writing TaskDaemon handlers.

Installation

dotnet add package TaskDaemon.Handler

Basic Usage

using TaskDaemon;

Handler.Run(task => {
    var name = task.task_data.GetValueOrDefault("name", "World");
    return new Success(new { message = $"Hello, {name}!" });
});

Task Record

public record Task(
    string task_id,
    string task_type,
    Dictionary<string, object> task_data,
    int attempt  // Current attempt (starts at 0)
);

Result Types

// Abstract base
public abstract record Result;

// Success with result
public record Success(object result) : Result;

// Error with message and optional retryable flag
public record Error(string error, bool retryable = false) : Result;
Usage:
// Success
return new Success(new { processed = true });

// Non-retryable error (default)
return new Error("Invalid input");

// Retryable error
return new Error("Service unavailable", retryable: true);

Complete Example

using TaskDaemon;
using System.Text.RegularExpressions;

Handler.Run(task => {
    var text = task.task_data.GetValueOrDefault("text", "")?.ToString() ?? "";
    
    // Count words
    var words = Regex.Matches(text, @"\S+").Count;
    
    // Count characters (excluding spaces)
    var chars = text.Count(c => !char.IsWhiteSpace(c));
    
    // Count lines
    var lines = text.Split('\n').Length;
    
    return new Success(new {
        words,
        characters = chars,
        lines
    });
});

Error Handling

Exceptions are automatically caught and converted to non-retryable errors:
Handler.Run(task => {
    // If this throws, it becomes Error(exception.Message)
    var required = task.task_data["required"];
    return new Success(new { value = required });
});
For explicit error handling:
Handler.Run(task => {
    if (!task.task_data.ContainsKey("required"))
        return new Error("Missing required field");
    
    try {
        var result = ExternalApiCall();
        return new Success(result);
    } catch (HttpRequestException e) {
        return new Error($"API unavailable: {e.Message}", retryable: true);
    }
});

Async Handlers

using TaskDaemon;

await Handler.RunAsync(async task => {
    var url = task.task_data["url"]?.ToString();
    
    using var client = new HttpClient();
    var response = await client.GetStringAsync(url);
    
    return new Success(new { data = response });
});

Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore
COPY . ./
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app
COPY --from=builder /app/out ./
CMD ["dotnet", "Handler.dll"]
For a self-contained single-file deployment:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /app
COPY . ./
RUN dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o out

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0
COPY --from=builder /app/out/Handler /handler
CMD ["/handler"]

Handler Configuration

[handlers.process]
image = "csharp-handler:latest"
instances = 4
timeout = 30

Requirements

  • .NET 6.0+ (uses records and file-scoped namespaces)
  • System.Text.Json for serialization (built-in)