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" />
                    
Directory.Packages.props
<PackageReference Include="SupplyDemand" />
                    
Project file
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
                    
#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
                    
Install as a Cake Addin
#tool nuget:?package=SupplyDemand&version=0.1.2
                    
Install as a Cake Tool

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 Demand calls (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

  • SupplierRegistry is a Dictionary<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 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.

Version Downloads Last Updated
0.1.2 161 7/11/2025
0.1.1 196 7/10/2025
0.1.0 204 7/10/2025
0.0.3 194 7/10/2025
0.0.2 194 7/10/2025
0.0.1 186 7/10/2025