Zooper.Bee
3.4.1
See the version list below for details.
dotnet add package Zooper.Bee --version 3.4.1
NuGet\Install-Package Zooper.Bee -Version 3.4.1
<PackageReference Include="Zooper.Bee" Version="3.4.1" />
<PackageVersion Include="Zooper.Bee" Version="3.4.1" />
<PackageReference Include="Zooper.Bee" />
paket add Zooper.Bee --version 3.4.1
#r "nuget: Zooper.Bee, 3.4.1"
#:package Zooper.Bee@3.4.1
#addin nuget:?package=Zooper.Bee&version=3.4.1
#tool nuget:?package=Zooper.Bee&version=3.4.1
Zooper.Bee
<img src="icon.png" alt="Zooper.Bee Logo" width="120" align="right"/>
A flexible and powerful railway-oriented programming library for .NET that allows you to define complex business processes with a fluent API.
Overview
Zooper.Bee lets you create railways that process requests and produce either successful results or meaningful errors. The library uses a builder pattern to construct railways with various execution patterns including sequential, conditional, parallel, and detached operations.
Key Concepts
- Railway: A sequence of operations that process a request to produce a result or error
- Request: The input data to the railway
- Payload: Data that passes through and gets modified by railway activities
- Success: The successful result of the railway
- Error: The errors result if the railway fails
Installation
dotnet add package Zooper.Bee
Getting Started
// Define a simple railway
var railway = new RailwayBuilder<Request, Payload, SuccessResult, ErrorResult>(
// Factory function that creates the initial payload from the request
request => new Payload { Data = request.Data },
// Selector function that creates the success result from the final payload
payload => new SuccessResult { ProcessedData = payload.Data }
)
.Validate(request =>
{
// Validate the request
if (string.IsNullOrEmpty(request.Data))
return Option<ErrorResult>.Some(new ErrorResult { Message = "Data is required" });
return Option<ErrorResult>.None;
})
.Do(payload =>
{
// Process the payload
payload.Data = payload.Data.ToUpper();
return Either<ErrorResult, Payload>.FromRight(payload);
})
.Build();
// Execute the railway
var result = await railway.Execute(new Request { Data = "hello world" }, CancellationToken.None);
if (result.IsRight)
{
Console.WriteLine($"Success: {result.Right.ProcessedData}"); // Output: Success: HELLO WORLD
}
else
{
Console.WriteLine($"Error: {result.Left.Message}");
}
Building Railways
Validation
Validates the incoming request before processing begins.
// Asynchronous validation
.Validate(async (request, cancellationToken) =>
{
var isValid = await ValidateAsync(request, cancellationToken);
return isValid ? Option<ErrorResult>.None : Option<ErrorResult>.Some(new ErrorResult());
})
// Synchronous validation
.Validate(request =>
{
var isValid = Validate(request);
return isValid ? Option<ErrorResult>.None : Option<ErrorResult>.Some(new ErrorResult());
})
Guards
Guards allow you to define checks that run before a railway begins execution. They're ideal for authentication, authorization, account validation, or any other requirement that must be satisfied before a railway can proceed.
// Asynchronous guard
.Guard(async (request, cancellationToken) =>
{
var isAuthorized = await CheckAuthorizationAsync(request, cancellationToken);
return isAuthorized ? Option<ErrorResult>.None : Option<ErrorResult>.Some(new ErrorResult());
})
// Synchronous guard
.Guard(request =>
{
var isAuthorized = CheckAuthorization(request);
return isAuthorized ? Option<ErrorResult>.None : Option<ErrorResult>.Some(new ErrorResult());
})
Benefits of Guards
- Guards run before creating the railway context, providing early validation
- They provide a clear separation between "can this railway run?" and the actual railway logic
- Common checks like authentication can be standardized and reused
- Failures short-circuit the railway, preventing unnecessary work
Activities
Activities are the building blocks of a railway. They process the payload and can produce either a success (with the modified payload) or an error.
// Asynchronous activity
.Do(async (payload, cancellationToken) =>
{
var result = await ProcessAsync(payload, cancellationToken);
return Either<ErrorResult, Payload>.FromRight(result);
})
// Synchronous activity
.Do(payload =>
{
var result = Process(payload);
return Either<ErrorResult, Payload>.FromRight(result);
})
// Multiple activities
.DoAll(
payload => DoFirstThing(payload),
payload => DoSecondThing(payload),
payload => DoThirdThing(payload)
)
Conditional Activities
Activities that only execute if a condition is met.
.DoIf(
payload => payload.ShouldProcess, // Condition
payload =>
{
// Activity that only executes if the condition is true
payload.Data = Process(payload.Data);
return Either<ErrorResult, Payload>.FromRight(payload);
}
)
Groups
Organize related activities into logical groups. Groups can have conditions and always merge their results back to the main railway.
.Group(
payload => payload.ShouldProcessGroup, // Optional condition
group => group
.Do(payload => FirstActivity(payload))
.Do(payload => SecondActivity(payload))
.Do(payload => ThirdActivity(payload))
)
Contexts with Local State
Create a context with the local state that is accessible to all activities within the context. This helps encapsulate related operations.
.WithContext(
null, // No condition, always execute
payload => new LocalState { Counter = 0 }, // Create local state
context => context
.Do((payload, state) =>
{
state.Counter++;
return (payload, state);
})
.Do((payload, state) =>
{
payload.Result = $"Counted to {state.Counter}";
return (payload, state);
})
)
Parallel Execution
Execute multiple groups of activities in parallel and merge the results.
.Parallel(
null, // No condition, always execute
parallel => parallel
.Group(group => group
.Do(payload => { payload.Result1 = "Result 1"; return payload; })
)
.Group(group => group
.Do(payload => { payload.Result2 = "Result 2"; return payload; })
)
)
Detached Execution
Execute activities in the background without waiting for their completion. Results from detached activities are not merged back into the main railway.
.Detach(
null, // No condition, always execute
detached => detached
.Do(payload =>
{
// This runs in the background
LogActivity(payload);
return payload;
})
)
Parallel Detached Execution
Execute multiple groups of detached activities in parallel without waiting for completion.
.ParallelDetached(
null, // No condition, always execute
parallelDetached => parallelDetached
.Detached(detached => detached
.Do(payload => { LogActivity1(payload); return payload; })
)
.Detached(detached => detached
.Do(payload => { LogActivity2(payload); return payload; })
)
)
Finally Block
Activities that always execute, even if the railway fails.
.Finally(payload =>
{
// Cleanup or logging
CleanupResources(payload);
return Either<ErrorResult, Payload>.FromRight(payload);
})
Advanced Patterns
Error Handling
.Do(payload =>
{
try
{
var result = RiskyOperation(payload);
return Either<ErrorResult, Payload>.FromRight(result);
}
catch (Exception ex)
{
return Either<ErrorResult, Payload>.FromLeft(new ErrorResult { Message = ex.Message });
}
})
Conditional Branching
Use conditions to determine which path to take in a railway.
.Group(
payload => payload.Type == "TypeA",
group => group
.Do(payload => ProcessTypeA(payload))
)
.Group(
payload => payload.Type == "TypeB",
group => group
.Do(payload => ProcessTypeB(payload))
)
Dependency Injection Integration
Zooper.Bee integrates seamlessly with .NET's dependency injection system. You can register all railway components with a single extension method:
// In Startup.cs or Program.cs
services.AddRailways();
This will scan all assemblies and register:
- All railway validations
- All railway activities
- All concrete railway classes (classes ending with "Railway")
You can also register specific components:
// Register only validations
services.AddRailwayValidations();
// Register only activities
services.AddRailwayActivities();
// Specify which assemblies to scan
services.AddRailways(new[] { typeof(Program).Assembly });
// Specify service lifetime (Singleton, Scoped, Transient)
services.AddRailways(lifetime: ServiceLifetime.Singleton);
Performance Considerations
- Use
Parallelfor CPU-bound operations that can benefit from parallel execution - Use
Detachfor I/O operations that don't affect the main railway - Be mindful of resource contention in parallel operations
- Consider using
WithContextto maintain state between related activities
Best Practices
- Keep activities small and focused on a single responsibility
- Use descriptive names for your railway methods
- Group related activities together
- Handle errors at appropriate levels
- Use
Finallyfor cleanup operations - Validate requests early to fail fast
- Use contextual state to avoid passing too many parameters
Migration from Workflow to Railway
As of the latest version, all Workflow classes have been renamed to Railway to better reflect the railway-oriented programming pattern used by the library. The old Workflow names are preserved as [Obsolete] shims for backward compatibility.
What changed
| Old Name | New Name |
|---|---|
Workflow<TRequest, TSuccess, TError> |
Railway<TRequest, TSuccess, TError> |
WorkflowBuilder<...> |
RailwayBuilder<...> |
WorkflowBuilderFactory |
RailwayBuilderFactory |
CreateWorkflow<...>() |
CreateRailway<...>() |
IWorkflowStep |
IRailwayStep |
IWorkflowValidation |
IRailwayValidation |
IWorkflowGuard |
IRailwayGuard |
AddWorkflows() |
AddRailways() |
AddWorkflowSteps() |
AddRailwaySteps() |
Backward compatibility
All old type names and extension methods are still available but marked with [Obsolete]. Your existing code will continue to compile and work, but you will see deprecation warnings encouraging you to migrate to the new names.
How to migrate
- Replace all
Workflow<type references withRailway< - Replace
WorkflowBuilder<withRailwayBuilder< - Replace
WorkflowBuilderFactory.CreateWorkflow<withRailwayBuilderFactory.CreateRailway< - Replace DI registration calls (
AddWorkflows()→AddRailways(), etc.) - Update any interface implementations (
IWorkflowStep→IRailwayStep, etc.)
License
MIT License (Copyright details here)
| 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. 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. |
-
.NETStandard 2.0
- Scrutor (>= 4.2.2)
- Zooper.Fox (>= 1.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Zooper.Bee:
| Package | Downloads |
|---|---|
|
Zooper.Bee.MediatR
A .NET library for building robust, functional railways (processing pipelines) using railway-oriented programming. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.1.0 | 42 | 5/31/2026 |
| 4.0.0 | 175 | 4/21/2026 |
| 3.5.0 | 203 | 4/1/2026 |
| 3.4.1 | 140 | 3/21/2026 |
| 3.4.0 | 154 | 3/10/2026 |
| 3.4.0-preview.12 | 61 | 3/10/2026 |
| 3.3.0 | 658 | 4/24/2025 |
| 3.3.0-preview.11 | 66 | 3/10/2026 |
| 3.3.0-preview.10 | 67 | 3/10/2026 |
| 3.2.1 | 267 | 4/24/2025 |
| 3.2.0 | 266 | 4/24/2025 |
| 3.1.0 | 217 | 4/23/2025 |
| 3.0.0 | 242 | 4/21/2025 |
| 2.2.0 | 233 | 4/21/2025 |
| 2.1.0 | 236 | 4/21/2025 |
| 2.0.0 | 161 | 4/19/2025 |
| 1.0.0 | 240 | 4/18/2025 |