SupplyDemand 0.1.2
dotnet add package SupplyDemand --version 0.1.2
NuGet\Install-Package SupplyDemand -Version 0.1.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SupplyDemand" Version="0.1.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SupplyDemand" Version="0.1.2" />
<PackageReference Include="SupplyDemand" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SupplyDemand --version 0.1.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: SupplyDemand, 0.1.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SupplyDemand@0.1.2
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SupplyDemand&version=0.1.2
#tool nuget:?package=SupplyDemand&version=0.1.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
SupplyDemand
SupplyDemand is a tiny yet powerful C# library for dynamic, scoped, asynchronous dependency composition.
- Compose dependencies with async supplier functions, in a strongly typed (generic) way.
- Override, add, or remove "suppliers" per operation or call tree.
- Make nested, context-aware demands throughout your call graph.
- Great for: rules engines, workflows, plugins, test injection, and flexible orchestration.
Installation
Add SupplyDemand to your project with NuGet:
dotnet add package SupplyDemand
Or with the Package Manager:
Install-Package SupplyDemand
Quick Example
using System;
using System.Threading.Tasks;
using SupplyDemand;
class Program
{
static async Task Main()
{
// 1. Define suppliers (async, strongly typed)
var getNumber = new Supplier<object, int>((data, scope) => Task.FromResult(10));
var getMessage = new Supplier<object, string>((data, scope) => Task.FromResult("Hello!"));
// The supplier registry must exist before sumSupplier can reference it in closure:
SupplierRegistry suppliers = null;
var sumSupplier = new Supplier<object, string>(async (data, scope) =>
{
int n = await scope.Demand<string, object, int>(new DemandProps<SupplierRegistry, string, object>
{
Type = "number",
Key = "number",
Suppliers = suppliers
});
string msg = await scope.Demand<string, object, string>(new DemandProps<SupplierRegistry, string, object>
{
Type = "message",
Key = "message",
Suppliers = suppliers
});
return $"{msg} Your number is {n}.";
});
suppliers = new SupplierRegistry
{
["number"] = getNumber,
["message"] = getMessage,
["sum"] = sumSupplier
};
var rootSupplier = new Supplier<object, string>((data, scope) =>
scope.Demand<string, object, string>(new DemandProps<SupplierRegistry, string, object>
{
Type = "sum",
Key = "sum",
Suppliers = suppliers
})
);
// 4. Run!
string result = await SupplyDemand.Init(rootSupplier, suppliers);
Console.WriteLine(result); // Output: Hello! Your number is 10.
}
}
Advanced: Contextual Supplier Overrides (Merging)
Need to override/replace a supplier for just one demand?
Just "clone" your registry, replace the supplier, and pass the new registry for that call!
// Make sure your SupplierRegistry class contains a copy constructor:
// public SupplierRegistry(IDictionary<string, object> dict) : base(dict) { }
var numberSupplier = new Supplier<object, int>((data, scope) => Task.FromResult(123));
var suppliers = new SupplierRegistry { ["number"] = numberSupplier };
var rootSupplier = new Supplier<object, int>(async (data, scope) =>
{
// Clone and replace just for this demand:
var customSuppliers = new SupplierRegistry(suppliers)
{
["number"] = new Supplier<object, int>((d, s) => Task.FromResult(999))
};
return await scope.Demand<string, object, int>(new DemandProps<SupplierRegistry, string, object>
{
Type = "number",
Key = "number",
Suppliers = customSuppliers
});
});
int result = await SupplyDemand.Init(rootSupplier, suppliers);
Console.WriteLine(result); // Output: 999
Pattern: Composing Suppliers
Suppliers can call other suppliers dynamically and combine their results:
var suppliers = new SupplierRegistry();
suppliers["double"] = new Supplier<int, int>((data, scope) => Task.FromResult(data * 2));
suppliers["greet"] = new Supplier<string, string>((data, scope) => Task.FromResult("Hello, " + data));
suppliers["sumDoubles"] = new Supplier<(int, int), int>(async (data, scope) =>
{
int a = await scope.Demand<string, int, int>(new DemandProps<SupplierRegistry, string, int>
{
Type = "double",
Key = "doubleA",
Data = data.Item1,
Suppliers = suppliers
});
int b = await scope.Demand<string, int, int>(new DemandProps<SupplierRegistry, string, int>
{
Type = "double",
Key = "doubleB",
Data = data.Item2,
Suppliers = suppliers
});
return a + b;
});
var rootSupplier = new Supplier<(int, int), int>((data, scope) =>
scope.Demand<string, (int, int), int>(new DemandProps<SupplierRegistry, string, (int, int)>
{
Type = "sumDoubles",
Key = "sum",
Data = data,
Suppliers = suppliers
}));
int output = await SupplyDemand.Init(rootSupplier, suppliers, (10, 11));
// output == 42
How It Works
- Suppliers are async functions, registered by name/type.
- A demand is a context-aware call: specify the supplier name (type/key), provide data, and optionally scope/override the registry.
- Suppliers receive a scope object, to make further, nested and contextually controlled
Demandcalls (with their own data and/or supplier registry). - The API is fully async and uses generics for strong type safety from registry through demand to result.
API Reference
Supplier
public class Supplier<TData, TReturn> : ISupplier<TData, SupplierRegistry, TReturn>
{
public Supplier(Func<TData, Scope<SupplierRegistry>, Task<TReturn>> func);
public Task<TReturn> Invoke(TData data, Scope<SupplierRegistry> scope);
}
SupplierRegistry
SupplierRegistryis aDictionary<string, object>that holds your named suppliers.- For merging/overriding:
public SupplierRegistry(IDictionary<string, object> dict) : base(dict) { }
Scope
- Represents the current demand context.
Demand<TType, TData, TReturn>(DemandProps<SupplierRegistry, TType, TData> props):
Strongly-typed demand for another supplier.
DemandProps
- For structured, type-safe demand requests.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.