Skip to content

Repository files navigation

ConductorSharp

A comprehensive .NET client library for Conductor workflow orchestration engine. Features a strongly-typed workflow builder DSL, task handlers, and quality-of-life additions for building robust workflow applications.

NuGetLicense: MIT

Note: This documentation has been AI generated and human reviewed.

AI Assistant Users: See SKILL.md for a condensed reference guide optimized for AI coding assistants. It provides quick-reference documentation for all task types, configuration options, and common patterns. This file follows the Agent Skills open standard for extending AI assistant capabilities.

Table of Contents

Installation

Core Packages

# API client for Conductor
dotnet add package ConductorSharp.Client
# Workflow engine with builder DSL, task handlers, and worker scheduling
dotnet add package ConductorSharp.Engine

Additional Packages

# Built-in tasks (WaitSeconds, ReadWorkflowTasks, C# Lambda Tasks)
dotnet add package ConductorSharp.Patterns
# Kafka-based task cancellation notifications
dotnet add package ConductorSharp.KafkaCancellationNotifier
# CLI tool for scaffolding task/workflow definitions
dotnet tool install --global ConductorSharp.Toolkit

Quick Start

1. Configure Services

usingConductorSharp.Engine.Extensions;usingMicrosoft.Extensions.Hosting;varbuilder=Host.CreateApplicationBuilder(args);builder.Services.AddConductorSharp(baseUrl:"http://localhost:8080").AddExecutionManager(maxConcurrentWorkers:10,sleepInterval:500,longPollInterval:100,domain:null,typeof(Program).Assembly).AddPipelines(pipelines =>{pipelines.AddRequestResponseLogging();pipelines.AddValidation();});builder.Services.RegisterWorkflow<MyWorkflow>();varhost=builder.Build();awaithost.RunAsync();

2. Define a Task Handler

usingConductorSharp.Engine.Builders.Metadata;usingConductorSharp.Engine;publicclassPrepareEmailRequest:IRequest<PrepareEmailResponse>{publicstringCustomerName{get;set;}publicstringAddress{get;set;}}publicclassPrepareEmailResponse{publicstringEmailBody{get;set;}}[OriginalName("EMAIL_prepare")]publicclassPrepareEmailHandler:TaskRequestHandler<PrepareEmailRequest,PrepareEmailResponse>{publicoverrideasyncTask<PrepareEmailResponse>Handle(PrepareEmailRequestrequest,CancellationTokencancellationToken){varbody=$"Hello {request.CustomerName} at {request.Address}!";returnnewPrepareEmailResponse{EmailBody=body};}}

3. Define a Workflow

usingConductorSharp.Engine.Builders;usingConductorSharp.Engine.Builders.Metadata;publicclassSendNotificationInput:WorkflowInput<SendNotificationOutput>{publicintCustomerId{get;set;}}publicclassSendNotificationOutput:WorkflowOutput{publicstringEmailBody{get;set;}}[OriginalName("NOTIFICATION_send")][WorkflowMetadata(OwnerEmail="team@example.com")]publicclassSendNotificationWorkflow:Workflow<SendNotificationWorkflow,SendNotificationInput,SendNotificationOutput>{publicSendNotificationWorkflow(WorkflowDefinitionBuilder<SendNotificationWorkflow,SendNotificationInput,SendNotificationOutput>builder):base(builder){}publicGetCustomerHandlerGetCustomer{get;set;}publicPrepareEmailHandlerPrepareEmail{get;set;}publicoverridevoidBuildDefinition(){_builder.AddTask(
wf =>wf.GetCustomer,
wf =>newGetCustomerRequest{CustomerId=wf.WorkflowInput.CustomerId});_builder.AddTask(
wf =>wf.PrepareEmail,
wf =>newPrepareEmailRequest{CustomerName=wf.GetCustomer.Output.Name,Address=wf.GetCustomer.Output.Address});_builder.SetOutput(wf =>newSendNotificationOutput{EmailBody=wf.PrepareEmail.Output.EmailBody});}}

Core Concepts

Workflow Definition

Workflows are defined by inheriting from Workflow<TWorkflow, TInput, TOutput>:

publicclassMyWorkflow:Workflow<MyWorkflow,MyWorkflowInput,MyWorkflowOutput>{publicMyWorkflow(WorkflowDefinitionBuilder<MyWorkflow,MyWorkflowInput,MyWorkflowOutput>builder):base(builder){}// Task properties - these become task references in the workflowpublicSomeTaskHandlerFirstTask{get;set;}publicAnotherTaskHandlerSecondTask{get;set;}publicoverridevoidBuildDefinition(){// Add tasks with strongly-typed input expressions_builder.AddTask(wf =>wf.FirstTask, wf =>newSomeTaskRequest{Input=wf.WorkflowInput.SomeValue});_builder.AddTask(wf =>wf.SecondTask, wf =>newAnotherTaskRequest{Input=wf.FirstTask.Output.Result});// Set workflow output_builder.SetOutput(wf =>newMyWorkflowOutput{Result=wf.SecondTask.Output.Value});}}

Task Handlers

[OriginalName("MY_TASK_name")]publicclassMyTaskHandler:TaskRequestHandler<MyTaskRequest,MyTaskResponse>{publicoverrideasyncTask<MyTaskResponse>Handle(MyTaskRequestrequest,CancellationTokencancellationToken){returnnewMyTaskResponse{/* ... */};}}

Input/Output Models

// Workflow I/OpublicclassMyWorkflowInput:WorkflowInput<MyWorkflowOutput>{publicstringCustomerId{get;set;}}publicclassMyWorkflowOutput:WorkflowOutput{publicstringResult{get;set;}}// Task I/OpublicclassMyTaskRequest:IRequest<MyTaskResponse>{[Required]publicstringInputValue{get;set;}}publicclassMyTaskResponse{publicstringOutputValue{get;set;}}

Task Input Specification

In Conductor, task inputs in workflows are specified using Conductor expressions with the format: ${SOURCE.input/output.JSONPath}. The SOURCE can be workflow or a task reference name in the workflow definition. input/output refers to the input of the workflow or output of the task. JSONPath is used to traverse the input/output object.

ConductorSharp generates these expressions automatically when writing workflows. Here's an example:

_builder.AddTask(
wf =>wf.PrepareEmail,
wf =>newPrepareEmailRequest{CustomerName=$"{wf.GetCustomer.Output.FirstName}{wf.GetCustomer.Output.LastName}",Address=wf.WorkflowInput.Address});

This is converted to the following Conductor input parameters specification:

"inputParameters": {
"customer_name": "${get_customer.output.first_name} ${get_customer.output.last_name}",
"address": "${workflow.input.address}"
}

Casting

When input/output parameters are of different types, casting can be used:

wf =>newPrepareEmailRequest{CustomerName=((FullName)wf.GetCustomer.Output.Name).FirstName,Address=(string)wf.GetCustomer.Output.Address}

This translates to:

"inputParameters": {
"customer_name": "${get_customer.output.name.first_name}",
"address": "${get_customer.output.address}"
}

Array Initialization

Array initialization is supported. Arrays can be typed or dynamic:

wf =>new(){Integers=new[]{1,2,3},TestModelList=newList<ArrayTaskInput.TestModel>{newArrayTaskInput.TestModel{String=wf.Input.TestValue},newArrayTaskInput.TestModel{String="List2"}},Models=new[]{newArrayTaskInput.TestModel{String="Test1"},newArrayTaskInput.TestModel{String="Test2"}},Objects=newdynamic[]{new{AnonymousObjProp="Prop"},new{Test="Prop"}}}

This translates to:

"inputParameters": {
"integers": [1, 2, 3],
"test_model_list": [
{
"string": "${workflow.input.test_value}"
},
{
"string": "List2"
}
],
"models": [
{
"string": "Test1"
},
{
"string": "Test2"
}
],
"objects": [
{
"anonymous_obj_prop": "Prop"
},
{
"test": "Prop"
}
]
}

Object Initialization

Object initialization is supported, including anonymous objects when initializing sub-properties:

wf =>new(){NestedObjects=newTestModel{Integer=1,String="test",Object=newTestModel{Integer=1,String="string",Object=new{NestedInput="1"}}}}

This translates to:

"inputParameters": {
"nested_objects": {
"integer": 1,
"string": "test",
"object": {
"integer": 1,
"string": "string",
"object": {
"nested_input": "1"
}
}
}
}

Indexing

Dictionary indexing is supported. Indexing using an indexer on arbitrary types is currently not supported:

wf =>new(){CustomerName=wf.WorkflowInput.Dictionary["test"].CustomerName,Address=wf.WorkflowInput.DoubleDictionary["test"]["address"]}

This translates to:

"inputParameters": {
"customer_name": "${workflow.input.dictionary['test'].customer_name}",
"address": "${workflow.input.double_dictionary['test']['address']}"
}

Workflow Name

You can embed the name of any workflow in task input specification using NamingUtil.NameOf<T>():

wf =>new(){Name=$"Workflow name: {NamingUtil.NameOf<StringInterpolation>()}",WfName=NamingUtil.NameOf<StringInterpolation>()}

This translates to:

"inputParameters": {
"name": "Workflow name: TEST_StringInterpolation",
"wf_name": "TEST_StringInterpolation"
}

Note: StringInterpolation has an attribute [OriginalName("TEST_StringInterpolation")] applied.

String Concatenation

String concatenation is supported. You can concatenate strings with numbers, input/output parameters, and interpolation strings:

wf =>new(){Input=1+"Str_"+"2Str_"+wf.WorkflowInput.Input+$"My input: {wf.WorkflowInput.Input}"+NamingUtil.NameOf<StringAddition>()+1}

This translates to:

"inputParameters": {
"input": "1Str_2Str_${workflow.input.input}My input: ${workflow.input.input}string_addition1"
}

Note: StringAddition has an attribute [OriginalName("string_addition")] applied.

Metadata Attributes

AttributeTargetDescription
[OriginalName("NAME")]ClassCustom task/workflow name in Conductor
[WorkflowMetadata(...)]ClassWorkflow metadata (OwnerEmail, OwnerApp, Description, FailureWorkflow)
[Version(n)]ClassVersion number for sub-workflow references
[TaskDomain("domain")]ClassAssign task to specific domain

Note: There is no task equivalent of the WorkflowMetadata attribute. The task metadata is configured when registering the task:

services.RegisterWorkerTask<MyTaskHandler>(options =>{options.OwnerEmail="team@example.com";options.Description="My task description";});

Task Types

Simple Task

_builder.AddTask(wf =>wf.MySimpleTask, wf =>newMySimpleTaskRequest{Input=wf.WorkflowInput.Value});

Sub-Workflow Task

publicSubWorkflowTaskModel<ChildWorkflowInput,ChildWorkflowOutput>ChildWorkflow{get;set;}_builder.AddTask(wf =>wf.ChildWorkflow, wf =>newChildWorkflowInput{CustomerId=wf.WorkflowInput.CustomerId});

Switch Task (Conditional Branching)

publicSwitchTaskModelSwitchTask{get;set;}publicTaskATaskInCaseA{get;set;}publicTaskBTaskInCaseB{get;set;}_builder.AddTask(
wf =>wf.SwitchTask,
wf =>newSwitchTaskInput{SwitchCaseValue=wf.WorkflowInput.Operation},newDecisionCases<MyWorkflow>{["caseA"]= builder =>builder.AddTask(wf =>wf.TaskInCaseA, wf =>newTaskARequest{}),["caseB"]= builder =>builder.AddTask(wf =>wf.TaskInCaseB, wf =>newTaskBRequest{}),DefaultCase= builder =>{/* default case tasks */}});

Dynamic Task

publicDynamicTaskModel<ExpectedInput,ExpectedOutput>DynamicHandler{get;set;}_builder.AddTask(
wf =>wf.DynamicHandler,
wf =>newDynamicTaskInput<ExpectedInput,ExpectedOutput>{TaskInput=newExpectedInput{CustomerId=wf.WorkflowInput.CustomerId},TaskToExecute=wf.WorkflowInput.TaskName// Task name resolved at runtime});

Dynamic Fork-Join Task

publicDynamicForkJoinTaskModelDynamicFork{get;set;}_builder.AddTask(
wf =>wf.DynamicFork,
wf =>newDynamicForkJoinInput{DynamicTasks=/* list of tasks */,DynamicTasksInput=/* corresponding inputs */});

Do-While Loop Task

publicDoWhileTaskModelDoWhile{get;set;}publicCustomerGetHandlerGetCustomer{get;set;}_builder.AddTask(
wf =>wf.DoWhile,
wf =>newDoWhileInput{Value=wf.WorkflowInput.Loops},"$.do_while.iteration < $.value",// Loop condition
builder =>{builder.AddTask(wf =>wf.GetCustomer, wf =>newCustomerGetRequest{CustomerId="CUSTOMER-1"});});

Note: ConductorSharp does not provide a strongly typed output for the DoWhile task, as can be seen from the implementation:

publicclassDoWhileTaskModel:TaskModel<DoWhileInput,NoOutput>{}

Lambda Task (JavaScript)

publicclassLambdaInput:IRequest<LambdaOutput>{publicstringValue{get;set;}}publicclassLambdaOutput{publicstringSomething{get;set;}}publicLambdaTaskModel<LambdaInput,LambdaOutput>LambdaTask{get;set;}_builder.AddTask(
wf =>wf.LambdaTask,
wf =>newLambdaInput{Value=wf.WorkflowInput.Input},script:"return { something: $.Value.toUpperCase() }"// JavaScript expression);

For context, in the above parameterized generic class LambdaTaskModel, the LambdaOutput instance is available as Output.Result.Something. This is less than ideal, but is the current way of things. Reasoning can be seen in the implementation:

publicabstractclassLambdaOutputModel<O>{publicOResult{get;set;}}publicabstractclassLambdaTaskModel<I,O>whereI:IRequest<O>{publicIInput{get;set;}publicLambdaOutputModel<O>Output{get;set;}}

Wait Task

publicWaitTaskModelWaitTask{get;set;}_builder.AddTask(
wf =>wf.WaitTask,
wf =>newWaitTaskInput{Duration="1h"}// or Until = "2024-01-01T00:00:00Z");

Terminate Task

publicTerminateTaskModelTerminateTask{get;set;}_builder.AddTask(
wf =>wf.TerminateTask,
wf =>newTerminateTaskInput{TerminationStatus="COMPLETED",WorkflowOutput=new{Result="Done"}});

Human Task

publicHumanTaskModel<HumanTaskOutput>HumanTask{get;set;}_builder.AddTask(
wf =>wf.HumanTask,
wf =>newHumanTaskInput<HumanTaskOutput>{/* ... */});

JSON JQ Transform Task

publicJsonJqTransformTaskModel<JqInput,JqOutput>TransformTask{get;set;}_builder.AddTask(
wf =>wf.TransformTask,
wf =>newJqInput{QueryExpression=".data | map(.name)",Data=wf.WorkflowInput.Items});

PassThrough Task (Raw Definition)

For tasks not covered by the builder:

_builder.AddTasks(newWorkflowTask{Name="CUSTOM_task",TaskReferenceName="custom_ref",Type="CUSTOM",InputParameters=newDictionary<string,object>{["key"]="value"}});

Optional Tasks

Mark tasks as optional (workflow continues on failure):

_builder.AddTask(wf =>wf.OptionalTask, wf =>newOptionalTaskRequest{}).AsOptional();

Configuration

Execution Manager

services.AddConductorSharp(baseUrl:"http://localhost:8080").AddExecutionManager(maxConcurrentWorkers:10,// Max concurrent task executionssleepInterval:500,// Base polling interval (ms)longPollInterval:100,// Long poll timeout (ms)domain:"my-domain",// Optional worker domaintypeof(Program).Assembly// Assemblies containing handlers);

Multiple Conductor Instances

services.AddConductorSharp(baseUrl:"http://primary-conductor:8080").AddAlternateClient(baseUrl:"http://secondary-conductor:8080",key:"Secondary",apiPath:"api",ignoreInvalidCertificate:false);// Usage with keyed servicespublicclassMyController(IWorkflowServiceprimaryService,[FromKeyedServices("Secondary")]IWorkflowServicesecondaryService){}

Poll Timing Strategies

// Default: Inverse exponential backoff.AddExecutionManager(...)// Constant interval polling.AddExecutionManager(...).UseConstantPollTimingStrategy()

Worker Task Registration

Register standalone tasks without workflow:

services.RegisterWorkerTask<MyTaskHandler>(options =>{options.OwnerEmail="team@example.com";options.Description="My task description";});

Pipeline Behaviors

Behaviors form a middleware pipeline for task execution (powered by MediatR):

.AddPipelines(pipelines =>{// Add custom behavior (runs first)pipelines.AddCustomBehavior(typeof(MyCustomBehavior<,>));// Built-in behaviorspipelines.AddExecutionTaskTracking();// Track task execution metricspipelines.AddContextLogging();// Add context to log scopespipelines.AddRequestResponseLogging();// Log requests/responsespipelines.AddValidation();// Validate using DataAnnotations})

Custom Behavior Example

publicclassTimingBehavior<TRequest,TResponse>:IPipelineBehavior<TRequest,TResponse>{publicasyncTask<TResponse>Handle(TRequestrequest,RequestHandlerDelegate<TResponse>next,CancellationTokencancellationToken){varsw=Stopwatch.StartNew();varresponse=awaitnext();Console.WriteLine($"Execution took {sw.ElapsedMilliseconds}ms");returnresponse;}}

Health Checks

ASP.NET Core Integration

// In Program.csbuilder.Services.AddHealthChecks().AddCheck<ConductorSharpHealthCheck>("conductor-worker");// Configure health service.AddExecutionManager(...).SetHealthCheckService<FileHealthService>()// or InMemoryHealthService

Available Health Services

ServiceDescription
InMemoryHealthServiceIn-memory health state (default)
FileHealthServicePersists health to CONDUCTORSHARP_HEALTH.json file

Execution Context

Access workflow/task metadata in handlers:

publicclassMyHandler:TaskRequestHandler<MyRequest,MyResponse>{privatereadonlyConductorSharpExecutionContext_context;publicMyHandler(ConductorSharpExecutionContextcontext){_context=context;}publicoverrideasyncTask<MyResponse>Handle(MyRequestrequest,CancellationTokencancellationToken){varworkflowId=_context.WorkflowId;vartaskId=_context.TaskId;varcorrelationId=_context.CorrelationId;// ...}}

Patterns Package

Additional built-in tasks and utilities:

.AddExecutionManager(...).AddConductorSharpPatterns()// Adds WaitSeconds, ReadWorkflowTasks.AddCSharpLambdaTasks()// Adds C# lambda task support

WaitSeconds Task

publicWaitSecondsWaitTask{get;set;}_builder.AddTask(wf =>wf.WaitTask, wf =>newWaitSecondsRequest{Seconds=30});

ReadWorkflowTasks Task

Read task data from another workflow:

publicReadWorkflowTasksReadTasks{get;set;}_builder.AddTask(
wf =>wf.ReadTasks,
wf =>newReadWorkflowTasksInput{WorkflowId=wf.WorkflowInput.TargetWorkflowId,TaskNames="task1,task2"// Comma-separated reference names});

C# Lambda Tasks

Execute C# code inline in workflows:

publicCSharpLambdaTaskModel<LambdaInput,LambdaOutput>InlineLambda{get;set;}_builder.AddTask(
wf =>wf.InlineLambda,
wf =>newLambdaInput{Value=wf.WorkflowInput.Input},
input =>newLambdaOutput{Result=input.Value.ToUpperInvariant()});

Signal Wait

The Signal Wait pattern allows workflows to pause execution until an external signal is received, without consuming worker threads. Useful for human approvals, external callbacks, or long-running async operations.

Setup

.AddExecutionManager(...).AddSignalWait<YourSignalStore>("MY_PREFIX")

Important: It is recommended to implement your own ISignalStore with a persistent backend (database, Redis, etc.). The built-in InMemorySignalStore is provided for development and testing only - it uses a static dictionary that only works within a single process and loses all data on restart.

Important: The purpose of 'MY_PREFIX' is to allow multiple microservices running on the same Conductor instance to have different signal stores and not accidentally poll eachothers tasks.

Using SignalWait in a Workflow

publicclassOrderWorkflow:Workflow<OrderWorkflow,OrderInput,OrderOutput>{publicSignalWaitWaitForPayment{get;set;}publicoverridevoidBuildDefinition(){// ... previous tasks ..._builder.AddTask(
wf =>wf.WaitForPayment,
wf =>newSignalWaitInput{SignalKey=$"payment:{wf.WorkflowInput.OrderId}"});// ... tasks after signal received ...}}

Sending Signals

publicclassPaymentController:ControllerBase{privatereadonlyISignalService_signalService;[HttpPost("webhook")]publicasyncTask<IActionResult>PaymentReceived(PaymentNotificationnotification){await_signalService.SendSignalAsync($"payment:{notification.OrderId}",TaskResultStatus.COMPLETED,newDictionary<string,object>{["transactionId"]=notification.TransactionId});returnOk();}}

Monitoring Pending Signals

varpending=await_signalStore.GetPendingWaitersAsync();

Architecture

The signal system consists of:

ComponentDescription
SignalWaitSub-workflow that registers a waiter and enters a WAIT task
RegisterWaiterTask that registers the workflow in the signal store
ISignalStorePersistence abstraction - implement with your own backend
ISignalServiceService for sending signals to waiting workflows
SignalSweeperServiceBackground service that reconciles signals with WAIT tasks
InMemorySignalStoreDevelopment-only in-memory implementation

Signals and waiters can arrive in any order - if a signal arrives before the workflow registers, the workflow will see it immediately and skip waiting.

Task Configuration

The RegisterWaiter task is registered with specific settings:

  • ConcurrentExecLimit = 1: Only one registration can execute at a time per worker, preventing race conditions when multiple workflows register simultaneously
  • RetryCount = 10 with RetryDelaySeconds = 1: Provides resilience against transient failures (e.g., database connection issues)

These settings mean that if many workflows start waiting at the same time, registrations will be serialized, which may introduce slight delays but ensures consistency in the signal store.

Kafka Cancellation Notifier

Handle task cancellation via Kafka events:

.AddExecutionManager(...).AddKafkaCancellationNotifier(kafkaBootstrapServers:"localhost:9092",topicName:"conductor.status.task",groupId:"my-worker-group",createTopicOnStartup:true)

appsettings.json:

{
"Conductor": {
"BaseUrl": "http://localhost:8080",
"MaxConcurrentWorkers": 10,
"SleepInterval": 500,
"LongPollInterval": 100,
"KafkaCancellationNotifier": {
"BootstrapServers": "localhost:9092",
"GroupId": "my-worker",
"TopicName": "conductor.status.task"
}
}
}

Toolkit CLI

Generate C# models from existing Conductor task/workflow definitions.

Installation

dotnet tool install --global ConductorSharp.Toolkit --version 4.0.1

Configuration

Create conductorsharp.yaml:

baseUrl: http://localhost:8080apiPath: apinamespace: MyApp.Generateddestination: ./Generated

Usage

# Scaffold all tasks and workflows
dotnet-conductorsharp
# Use custom config file
dotnet-conductorsharp -f myconfig.yaml
# Filter by name
dotnet-conductorsharp -n CUSTOMER_get -n ORDER_create
# Filter by owner email
dotnet-conductorsharp -e team@example.com
# Filter by owner app
dotnet-conductorsharp -a my-application
# Skip tasks or workflows
dotnet-conductorsharp --no-tasks
dotnet-conductorsharp --no-workflows
# Preview without generating files
dotnet-conductorsharp --dry-run

Command Options

OptionDescription
-f, --fileConfiguration file path (default: conductorsharp.yaml)
-n, --nameFilter by task/workflow name (can specify multiple)
-a, --appFilter by owner app
-e, --emailFilter by owner email
--no-tasksSkip task scaffolding
--no-workflowsSkip workflow scaffolding
--dry-runPreview what would be generated

API Services

Inject these services to interact with Conductor programmatically:

ServiceDescription
IWorkflowServiceStart, pause, resume, terminate workflows
ITaskServiceUpdate tasks, get logs, poll for tasks
IMetadataServiceManage workflow/task definitions
IAdminServiceAdmin operations, queue management
IEventServiceEvent handlers
IQueueAdminServiceQueue administration
IWorkflowBulkServiceBulk workflow operations
IHealthServiceConductor server health
IExternalPayloadServiceExternal payload storage

Example Usage

publicclassWorkflowController:ControllerBase{privatereadonlyIWorkflowService_workflowService;privatereadonlyIMetadataService_metadataService;publicWorkflowController(IWorkflowServiceworkflowService,IMetadataServicemetadataService){_workflowService=workflowService;_metadataService=metadataService;}[HttpPost("start")]publicasyncTask<string>StartWorkflow([FromBody]StartRequestrequest){returnawait_workflowService.StartAsync(newStartWorkflowRequest{Name="MY_workflow",Version=1,Input=newDictionary<string,object>{["customerId"]=request.CustomerId}});}[HttpGet("definitions")]publicasyncTask<ICollection<WorkflowDef>>GetDefinitions(){returnawait_metadataService.ListWorkflowsAsync();}}

Running the Examples

Prerequisites

  1. Clone and run Conductor:

    git clone https://github.com/conductor-oss/conductor.git
    cd conductor
    docker-compose up -d
  2. Conductor UI available at: http://localhost:5000 (may vary by version)

Starting the Examples

The solution includes three example projects:

ProjectDescription
ConductorSharp.DefinitionsConsole app with workflow definitions
ConductorSharp.ApiEnabledWeb API with workflow execution endpoints
ConductorSharp.NoApiConsole app with Kafka cancellation support
# Run with Docker Compose
docker-compose up
# Or run individual projectscd examples/ConductorSharp.Definitions
dotnet run

General Notes

Events Not Supported

The Conductor events are currently not supported by the library.

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

No description, website, or topics provided.

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages