A powerful, type-safe .NET library ecosystem for working with graph data structures and graph databases. GraphModel provides a clean abstraction layer over graph databases with advanced LINQ querying, transaction management, and relationship traversal capabilities.
- π Type-Safe Graph Operations - Work with strongly-typed nodes and relationships using modern C# features
- π Advanced LINQ Support - Query your graph using familiar LINQ syntax with graph-specific extensions
- π Graph Traversal & Path Finding - Navigate complex relationships with depth control and direction constraints
- β‘ Transaction Management - Full ACID transaction support with async/await patterns
- π― Provider Architecture - Clean abstraction supporting multiple graph database backends
- π Neo4j Integration - Complete Neo4j implementation with LINQ-to-Cypher translation
- π‘οΈ Compile-Time Validation - Code analyzers ensure that the data model requirements
- ποΈ Complex Object Serialization - Automatic handling of complex properties and circular references
- π Build-time code generation - Automatic code generation for efficient serialization/deserialization of domain data types
- π¨ Attribute-Based Configuration - Configure nodes and relationships using intuitive attributes
To get started, you only need to install the Neo4j provider package:
# Install the Neo4j provider (required)
dotnet add package Cvoya.Graph.Model.Neo4j
# Optionally, add code analyzers for extra compile-time validation (recommended)
dotnet add package Cvoya.Graph.Model.Analyzersusing Cvoya.Graph.Model;
[Node("Person")]
public class Person : INode
{
public string Id { get; set; } = Guid.NewGuid().ToString();
[Property("first_name", Index = true)]
public string FirstName { get; set; } = string.Empty;
[Property("last_name", Index = true)]
public string LastName { get; set; } = string.Empty;
// The Property attribute is optional
public int Age { get; set; }
public Address? HomeAddress { get; set; } // Complex types supported
}
// When used as the type of a property, it makes that property a "complex property"
public class Address
{
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
public string Country { get; set; } = string.Empty;
public Point? Coordinates { get; set; } // Spatial data
}
[Relationship("KNOWS")]
public class Knows : IRelationship
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string StartNodeId { get; set; } = string.Empty;
public string EndNodeId { get; set; } = string.Empty;
public RelationshipDirection Direction { get; init; } = RelationshipDirection.Outgoing;
public DateTime Since { get; set; }
// Relationships cannot have complex properties
}For your convenience, the Cvoya.Graph.Model package also offers Node and Relationship records so that you only have to focus on your domain-specific properties:
public record Person : Node
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public int Age { get; set; }
public Address? HomeAddress { get; set; }
}
public record Knows : Relationship
{
public DateTime Since { get; set; }
}
// or
public record Knows : Relationship
{
public Knows() : base(Guid.NewGuid().ToString("N"), Guid.NewGuid().ToString("N")) { }
public Knows(Person p1, Person p2) : base(p1.Id, p2.Id) {}
public DateTime Since { get; set; }
}using Cvoya.Graph.Model.Neo4j;
// Neo4j provider
var store = new Neo4jGraphStore(
uri: "bolt://localhost:7687",
username: "neo4j",
password: "password",
databaseName: "myapp"
);
var graph = store.Graph;// Create nodes
var alice = new Person
{
FirstName = "Alice",
LastName = "Smith",
Age = 30,
HomeAddress = new Address
{
Street = "123 Main St",
City = "Portland",
Country = "USA"
}
};
await graph.CreateNodeAsync(alice);
// Create relationships
var bob = new Person { FirstName = "Bob", LastName = "Jones", Age = 25 };
await graph.CreateNodeAsync(bob);
var friendship = new Knows(alice, bob)
{
Since = DateTime.UtcNow.AddYears(-2)
};
await graph.CreateRelationshipAsync(friendship);
// Query with LINQ
var youngPeople = await graph.Nodes<Person>()
.Where(p => p.Age < 30)
.Where(p => p.HomeAddress != null && p.HomeAddress.City == "Portland")
.OrderBy(p => p.FirstName)
.ToListAsync();
// Graph traversal
var alicesFriends = await graph.Nodes<Person>()
.Where(p => p.FirstName == "Alice")
.Traverse<Person, Knows, Person>()
.WithDepth(1, 2)
.Where(friend => friend.Age > 20)
.ToListAsync();await using var transaction = await graph.GetTransactionAsync();
try
{
await graph.CreateNodeAsync(person, transaction: transaction);
await graph.CreateRelationshipAsync(relationship, transaction: transaction);
await transaction.Commit();
}
catch
{
await transaction.Rollback();
throw;
}- Core Concepts - Understanding nodes, relationships, and entities
- LINQ Querying - Advanced query patterns and graph traversal
- Transaction Management - Working with ACID transactions
- Attributes & Configuration - Customizing nodes and relationships
- Best Practices - Performance tips and patterns
- Neo4j Provider - Neo4j-specific features and configuration
- Code Analyzers - Compile-time validation rules
- Code Generation - Compile-time validation rules
- API Reference - API documentation generated from the source code
- Troubleshooting - In case you encounter issues
- Building Graph Model - Building the projects in this repository
Explore comprehensive examples in the examples/ directory:
- Basic Serialization - CRUD operations and complex object handling
- Basic CRUD - Fundamental create, read, update, delete operations
- LINQ & Traversal - Advanced querying and graph navigation
- Transaction Management - ACID transactions and rollback scenarios
- Advanced Scenarios - Complex patterns and optimizations
- Social Network - Real-world social graph implementation
GraphModel supports multiple build configurations for different scenarios:
# Development (fastest, project references)
dotnet build --configuration Debug
# Local package testing (test package references before publishing)
dotnet build --configuration LocalFeed
# Production builds (package references, requires VERSION file)
dotnet build --configuration ReleaseFor testing package references locally before publishing to NuGet:
# Method 1: Direct LocalFeed build
dotnet build --configuration LocalFeed
dotnet build --configuration Release
# Method 2: Using helper script
./scripts/setup-local-feed-msbuild.sh
dotnet build --configuration Release
# Cleanup when done
dotnet msbuild -target:CleanLocalFeedSee Build System Documentation for complete details.
GraphModel follows a clean, layered architecture:
βββββββββββββββββββββββββββββββββββ
β Your Application β
βββββββββββββββββββββββββββββββββββ€
β Graph.Model (Core) β β Abstractions & LINQ
βββββββββββββββββββββββββββββββββββ€
β Graph.Model.Neo4j β β Provider Implementation
βββββββββββββββββββββββββββββββββββ€
β Neo4j Database β β Storage Layer
βββββββββββββββββββββββββββββββββββ
Key Components:
- IGraph - Main entry point for all graph operations
- INode / IRelationship - Type-safe entity contracts
- IGraphQueryable - LINQ provider with graph-specific extensions
- IGraphTransaction - ACID transaction management
- Attributes - Declarative configuration (Node, Relationship, Property)
- .NET 9.0 or .NET 10.0 or later
- Neo4j 4.0+ (5.x recommended for Neo4j provider)
- C# 12 language features
- Blog Post: GraphModel: A .NET Abstraction for Graphs
- Blog Post: Playing with graphs and neo4j by Savas Parastatidis
- Neo4j Documentation
- Graph Database Concepts
We welcome contributions! Please see our Contributing Guidelines for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Special thanks to the Neo4j team for creating an excellent graph database and driver ecosystem that makes this library possible.
Built with β€οΈ by Savas Parastatidis