Installation
Basic usage
Loader
Theloader argument accepts a ZenLoader that resolves decisions by key. Use ZenLoader.Callback to load from any storage backend, or a configuration variant (Static, Filesystem, Zip) to pre-load and pre-compile all decisions when you create the engine.
Loader configurations
Prefer a configuration when your decisions are known up front. The engine compiles them once, so evaluations skip loading and parsing entirely.File system
Implement theZenDecisionLoaderCallback interface with a Task<JsonBuffer?> Load(string key) method and wrap it in ZenLoader.Callback. Return null when the decision does not exist. Use ConcurrentDictionary to cache decisions for optimal performance.
AWS S3
Azure Blob Storage
Google Cloud Storage
Async evaluation
Evaluation methods returnTask for native async/await integration:
Batch evaluation
UseEvaluateBatch to evaluate many contexts in a single call. Each request pairs a decision key with a context, and each result reports its own success or error:
Error handling
Tracing
Enable tracing to inspect decision execution:Expression utilities
Evaluate ZEN expressions outside of a decision context:Custom nodes
Extend the engine with custom logic by implementingZenCustomNodeCallback:
Performance note
The C# bindings use UniFFI with P/Invoke for interoperability with the native Rust engine. This introduces some overhead compared to native Rust. Native libraries are bundled for Windows (x64), macOS (x64/ARM), and Linux (x64/ARM).
Best practices
Useusing for resource management. ZenEngine, ZenDecision, and ZenExpression implement IDisposable to release native resources.
ZenEngine instance at application startup and reuse it for all evaluations.
Implement a loader for dynamic decisions. The loader pattern centralizes decision loading logic and enables caching with ConcurrentDictionary.
Use Task.WhenAll for parallel evaluation. Evaluate multiple decisions concurrently with async/await.