Graphify 1.0.0-rc.13
dotnet add package Graphify --version 1.0.0-rc.13
NuGet\Install-Package Graphify -Version 1.0.0-rc.13
<PackageReference Include="Graphify" Version="1.0.0-rc.13"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Graphify" Version="1.0.0-rc.13" />
<PackageReference Include="Graphify"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Graphify --version 1.0.0-rc.13
#r "nuget: Graphify, 1.0.0-rc.13"
#:package Graphify@1.0.0-rc.13
#addin nuget:?package=Graphify&version=1.0.0-rc.13&prerelease
#tool nuget:?package=Graphify&version=1.0.0-rc.13&prerelease
Graphify

Graphify is a .NET Roslyn source generator that turns your object models into navigable graphs. It enables engineers to scan an object hierarchy and observe values at different levels without reflection by generating strongly-typed nodes.
Why Graphify
- Graph scanning without reflection: Traverse and observe your object graphs with generated, strongly-typed nodes.
- Type-safe visitors: Register and compose visitors for specific nodes in the graph.
- Generator-backed performance: Build-time code generation avoids runtime discovery costs.
- Analyzer guardrails: Diagnostics catch misconfiguration early.
Requirements
- .NET SDK with a Roslyn-capable compiler (Visual Studio 2022, Rider, or VS Code with the C# extension).
- A C# language version that supports source generators and async streams (C# 8.0 or later).
Installation
Add the package reference to your project:
<ItemGroup>
<PackageReference Include="Graphify" Version="<LATEST_VERSION>" />
</ItemGroup>
Or via the package manager console:
Install-Package Graphify
Quick Start
Annotate a partial type with the Graphify attribute to generate graph nodes and visitor hooks.
using Graphify;
[Graphify]
public sealed partial class Order
{
public Customer Customer { get; init; } = new();
public decimal Total { get; init; }
}
With generated graph nodes, you can register visitors for specific paths (for example, observing the Total or Customer) without using reflection. The generated APIs give you strongly-typed entry points into the object graph.
Key Concepts
Graphify generates a few key building blocks. Understanding these makes it easier to wire up scanning and observation in your application.
Model
The model is your domain object graph annotated with the Graphify attribute. Any public instance properties become navigation points in the graph. The model is generated within the scope of the annotated type, within a subclass named Graph. Each tier within the object graph is nested within the definition of the previous, providing an intuitive structure.
So when using Order as defined above and we define Customer as:
public sealed class Customer
{
public string Email { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
}
Taking the Name node for brevity, the following structure is generated to allow observation:
partial class Order
{
public sealed partial class Graph
{
public sealed partial class Customer
{
public sealed partial class Name
{
internal Name(Order root, Customer customer, string Value)
{
Root = root;
Customer = customer;
Value = Value;
}
public Order Root { get; private set; }
public Customer Customer { get; private set; }
public string Value { get; private set; }
}
}
}
}
Traverse
Use the Traverse attribute to control how Graphify walks specific properties. The Scope property defaults to TraverseScope.All, which behaves the same as if the attribute is not present. Set Scope to TraverseScope.None to exclude the property entirely, or to TraverseScope.Property to include the property itself while skipping any child properties (for collections, elements are still enumerated, but their child properties are not traversed).
public sealed class Customer
{
[Traverse(Scope = TraverseScope.Property)]
public Address Address { get; init; } = new();
}
Visitors
Visitors allows for the observation of specific nodes within the heirarchy. You implement a visitor by targeting the node type you care about and returning the appropriate observation(s). This makes it easy to capture the exact data points you need without manual tree-walking.
The following demonstrates a visitor that observes the Email value from the Customer node:
using Graphify;
public sealed class CustomerEmailVisitor(IBlacklist blacklist)
: IVisitor<Order.Graph.Customer.Email>
{
public async IAsyncEnumerable<string> Observe(Order.Graph.Customer.Email instance, [EnumeratorCancellation] CancellationToken cancellationToken))
{
if (blacklist.Contains(instance.Value))
{
yield return instance.Value;
}
}
}
Navigator
The navigator is the generated entry point for scanning the graph. It coordinates traversal from the root node through child nodes, invoking any visitors registered for each node. Because the traversal logic is generated, there is no reflection and no runtime discovery cost. The navigator is specific to the type annotated with Graphify with an interface and implementation generated alongside the annotated type.
For the Order example, the following navigator interface will be IOrderNavigator with the internal implementation being OrderNavigator. The implementation uses the IServiceProvider to resolve visitors as it traverses the graph.
public sealed class OrderVerificationService(IOrderNavigator navigator)
{
public async IAsyncEnumerable<string> Verify(Order[] orders, [EnumeratorCancellation] CancellationToken cancellationToken)
{
foreach (Order order in orders)
{
await foreach (string email in navigator.Navigate<string>(order, cancellationToken))
{
yield return email;
}
}
}
}
IoC registration
Graphify generates an extension method to support registration of the navigator for dependency injection whenever the project within which the annotated type references IServiceCollection from Microsoft.Extensions.DependencyInjection. This allows you to compose the navigator with your existing application services and control their lifetime centrally.
For the Order example, the navigator can be registered as follows:
_ = services.AddOrderNavigator();
The service will be registered as a singleton on IOrderNavigator and INavigator<Order>.
Typical usage flow
- Annotate a Model with
Graphifyto generate the graph nodes and navigator. - Register Navigator using the generated IoC extension methods.
- Implement Visitors for nodes you want to observe.
- Register Visitors using the same container that the navigator.
- Scan the Graph using the navigator to gather observations.
This flow keeps traversal explicit, type-safe, and discoverable in IntelliSense.
Analyzers
Graphify ships analyzers to keep your graph models consistent and safe:
| Rule ID | Category | Severity | Description |
|---|---|---|---|
| GRAFY01 | Usage | Warning | Type is not compatible with Graphify |
| GRAFY02 | Usage | Warning | Type is not Partial |
| GRAFY03 | Usage | Info | Type does not utilize Graphify |
Contributing
Contributions are welcome. See CONTRIBUTING.md for build instructions and coding guidelines.
License
This project is licensed under the MIT License. See LICENSE.md for details.
| 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
- Microsoft.CodeAnalysis.Analyzers (>= 3.11.0 && < 3.12.0)
- System.Collections.Immutable (>= 10.0.2)
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.0.0-rc.13 | 64 | 2/10/2026 |