DagLib 1.1.1

dotnet add package DagLib --version 1.1.1
                    
NuGet\Install-Package DagLib -Version 1.1.1
                    
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="DagLib" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DagLib" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="DagLib" />
                    
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 DagLib --version 1.1.1
                    
#r "nuget: DagLib, 1.1.1"
                    
#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 DagLib@1.1.1
                    
#: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=DagLib&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=DagLib&version=1.1.1
                    
Install as a Cake Tool

NuGet Version NuGet Downloads License: MIT

DagLib

A generic DAG (directed acyclic graph) library for .NET, built around Guid-based node identity with cycle detection, topological sorting, and ancestor/descendant traversal baked in. It's designed to sit behind immediate-mode node editor UIs like ImNodes, but has no dependency on any UI library itself.

Why

Node editors need a graph model that's easy to mutate interactively — add a node here, drag a link there — while never letting the underlying structure become invalid. DagLib rejects edges that would create a cycle at the moment you try to add them, so the UI layer never has to deal with an inconsistent graph in the first place. A full-graph cycle scan is also available for the one case interactive checks can't cover: data coming in from an external source, like a save file.

Features

  • Typed payloadsNode<T> and DagGraph<T> let each node carry any data type: a label, a record with position and color, a delegate, whatever the application needs.
  • Two layers of cycle safety — a cheap, single-edge check (WouldCreateCycle) for interactive use, and a full-graph sweep (HasCycle) for validating data loaded from elsewhere.
  • Topological sort via Kahn's algorithm.
  • Traversal helpersGetAncestors and GetDescendants for impact analysis ("what does this feed into?" / "what feeds into this?").
  • ID bridgingNodeIdMapper maps each node's persistent Guid to a throwaway int for int-keyed APIs like ImNodes, without ever discarding the original identity.
  • ImNodes-facing helperImNodesGraphView<T> combines a DagGraph<T> and a NodeIdMapper into a single facade for wiring up ImNodes events by int ID, so glue code isn't duplicated at every call site.
  • JSON persistenceDagLib.Serialization.GraphSerializer saves and loads graphs as JSON, encoding edges as array indices rather than repeated Guids to keep files small, with an optional GZip-compressed format for larger graphs.

Install

dotnet add package DagLib

Usage

using DagLib;

var graph = new DagGraph<string>();
var a = graph.AddNode("A");
var b = graph.AddNode("B");
var c = graph.AddNode("C");

graph.AddEdge(a, b);
graph.AddEdge(a, c);

var order = graph.TopologicalSort();

Saving and loading JSON

using DagLib.Serialization;

GraphSerializer.Save(graph, "graph.json");
GraphSerializer.Save(graph, "graph.json.gz", compress: true);

var loaded = GraphSerializer.Load<string>("graph.json");

Working with ImNodes

using DagLib;

var mapper = new NodeIdMapper();
int imNodesId = mapper.GetOrCreateImNodesId(a.Id);
// pass imNodesId into the native API, e.g. ImNodes.BeginNode(imNodesId);

For a tighter integration, ImNodesGraphView<T> wraps a DagGraph<T> and a NodeIdMapper together, so ImNodes event handlers can work directly with int IDs instead of manually translating back and forth. Besides the constructor that takes an existing graph and mapper, two lighter-weight constructors are available for common setups: one that takes no arguments and creates both a new DagGraph<T> and a new NodeIdMapper internally, and one that takes only an existing graph and creates a fresh NodeIdMapper for it (handy when reopening a graph loaded from storage in a new UI session).

using DagLib;

var view = new ImNodesGraphView<string>(graph, mapper); // wrap an existing graph and mapper
var view = new ImNodesGraphView<string>(graph);          // wrap an existing graph, fresh mapper
var view = new ImNodesGraphView<string>();               // start from scratch

// Handling an IsLinkCreated event
view.AddEdge(fromImNodesId, toImNodesId);

// Handling a node deletion — removes it from both the graph and the ID mapping
view.RemoveNode(imNodesId);

// Driving a render loop
foreach (var (node, id) in view.GetRenderableNodes())
{
    ImNodes.BeginNode(id);
    // ... build UI from node.Data ...
    ImNodes.EndNode();
}

Step-by-step traversal (branching execution)

TopologicalSort() resolves an entire execution order up front, which works well for pure data-flow graphs but doesn't fit graphs with runtime branching — an if/else node, a sequence node — where the next step depends on a condition that isn't known ahead of time. For those cases, GetNextNodes walks the graph one step at a time instead:

var pending = new Queue<Node<ExecNode>>();
pending.Enqueue(start);

while (pending.Count > 0)
{
    var node = pending.Dequeue();
    node.Data.Run?.Invoke();

    // No selector: just follow every outgoing edge as-is.
    // Pass a selector to pick a subset instead — e.g. only one branch of an if/else.
    var next = node == branch
        ? graph.GetNextNodes(node, (n, outputs) => [conditionResult ? outputs[0] : outputs[1]])
        : graph.GetNextNodes(node);

    foreach (var n in next)
        pending.Enqueue(n);
}

A selector can also return more than one node — useful for a "sequence" node that should continue down every outgoing edge at once rather than picking a single branch.

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
1.1.1 35 9/21/2026
1.1.0 37 9/21/2026
1.0.1 36 9/20/2026
1.0.0 79 9/12/2026