Stardust.Paradox.Data 2.3.20

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Stardust.Paradox.Data --version 2.3.20
                    
NuGet\Install-Package Stardust.Paradox.Data -Version 2.3.20
                    
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="Stardust.Paradox.Data" Version="2.3.20" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Stardust.Paradox.Data" Version="2.3.20" />
                    
Directory.Packages.props
<PackageReference Include="Stardust.Paradox.Data" />
                    
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 Stardust.Paradox.Data --version 2.3.20
                    
#r "nuget: Stardust.Paradox.Data, 2.3.20"
                    
#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 Stardust.Paradox.Data@2.3.20
                    
#: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=Stardust.Paradox.Data&version=2.3.20
                    
Install as a Cake Addin
#tool nuget:?package=Stardust.Paradox.Data&version=2.3.20
                    
Install as a Cake Tool

Stardust.Paradox.Data

An Entity Framework-styled tool for accessing Gremlin-based graph databases like Azure Cosmos DB and Apache TinkerPop.

Overview

Stardust.Paradox.Data provides a modern, type-safe approach to working with graph databases using Gremlin traversals. It offers an intuitive API similar to Entity Framework for relational databases, but designed specifically for graph data structures.

Key Features

  • Entity Framework-like API for graph databases
  • Multi-targeting support for .NET Standard 2.0, .NET 6, 7, 8, and 9
  • Type-safe Gremlin queries with LINQ-style syntax
  • Support for Azure Cosmos DB and Apache TinkerPop
  • Vertex and Edge modeling with attributes and relationships
  • Code generation for graph entity classes
  • Dependency injection integration
  • Change tracking and batch operations
  • Fluent configuration for relationships
  • In-memory testing with comprehensive scenario framework

Installation

dotnet add package Stardust.Paradox.Data

Quick Start

1. Define Your Graph Entities

using Stardust.Paradox.Data.Annotations;
using Stardust.Paradox.Data.Annotations.DataTypes;

[VertexLabel("person")]
public interface IPerson : IVertex
{
    string Id { get; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string Email { get; set; }
    string Name { get; set; }
    bool Adult { get; set; }
    DateTime LastUpdated { get; set; }
    EpochDateTime LastUpdatedEpoch { get; set; }
    
    // Navigation properties for graph relationships
    IEdgeCollection<IPerson> Parents { get; }
    IEdgeCollection<IPerson> Children { get; }
    
    [ToWayEdgeLabel("spouse")]
    IEdgeReference<IPerson> Spouse { get; }
    
    IEdgeCollection<ICompany> Employers { get; }
    
    // Custom traversal query
    [GremlinQuery("g.V('{id}').as('s').in('parent').out('parent').where(without('s')).dedup()")]
    IEdgeCollection<IPerson> Siblings { get; }
    
    // Inline serialized collections
    [InlineSerialization(SerializationType.ClearText)]
    ICollection<string> ProgrammingLanguages { get; }
}

[VertexLabel("company")]
public interface ICompany : IVertex
{
    string Id { get; }
    string Name { get; set; }
    string Industry { get; set; }
    
    // Reverse navigation property
    IEdgeCollection<IPerson> Employees { get; }
    
    [InlineSerialization(SerializationType.Base64)]
    ICollection<string> EmailDomains { get; }
}

[EdgeLabel("employment")]
public interface IEmployment : IEdge<IPerson, ICompany>, IDynamicGraphEntity
{
    string Id { get; }
    DateTime HiredDate { get; set; }
    string Manager { get; set; }
    
    [InlineSerialization(SerializationType.ClearText)]
    ICollection<string> Benefits { get; set; }
}

2. Configure Your Graph Context

using Stardust.Paradox.Data;
using Microsoft.Extensions.DependencyInjection;

public class MyGraphContext : GraphContextBase
{
    static MyGraphContext()
    {
        PartitionKeyName = "pk"; // For CosmosDB compatibility
    }
    
    public MyGraphContext(IGremlinLanguageConnector connector) 
        : base(connector, CreateServiceProvider()) 
    { 
    }
    
    private static IServiceProvider CreateServiceProvider()
    {
        var services = new ServiceCollection();
        
        // Register entity binding for code generation
        services.AddEntityBinding((entity, implementation) => 
        {
            services.AddTransient(entity, implementation);
        });
        
        return services.BuildServiceProvider();
    }
    
    public IGraphSet<IPerson> People => GraphSet<IPerson>();
    public IGraphSet<ICompany> Companies => GraphSet<ICompany>();
    public IEdgeGraphSet<IEmployment> Employments => EdgeGraphSet<IEmployment>();
    
    protected override bool InitializeModel(IGraphConfiguration configuration)
    {
        configuration
            .ConfigureCollection<IPerson>()
                .In(p => p.Parents, "parent").Out<IPerson>(p => p.Children)
                .Out(p => p.Employers, "employer").In<ICompany>(c => c.Employees)
            .ConfigureCollection<ICompany>()
                .In(c => c.Employees, "employer").Out<IPerson>(p => p.Employers)
            .ConfigureCollection<IEmployment>();
            
        return true;
    }
}

3. Use the Context

// Dependency injection setup
services.AddSingleton<IGremlinLanguageConnector>(provider =>
    new GremlinNetLanguageConnector("hostname", "database", "collection", "key"));
services.AddScoped<MyGraphContext>();

// Usage in your application
using var context = serviceProvider.GetService<MyGraphContext>();

// Create and save new entities
var person = context.CreateEntity<IPerson>("person-1");
person.FirstName = "John";
person.LastName = "Doe";
person.Adult = true;

var company = context.CreateEntity<ICompany>("company-1");
company.Name = "Microsoft";
company.Industry = "Technology";

// Create relationships using EdgeGraphSet
var employment = context.Employments.Create(person, company);
employment.HiredDate = DateTime.UtcNow.AddYears(-2);
employment.Manager = "Jane Smith";

await context.SaveChangesAsync();

// Query entities using GraphSet methods
var retrievedPerson = await context.People.GetAsync("person-1");
var allPeople = await context.People.AllAsync();
var techWorkers = await context.PeopleProfiles.GetAsync(g=>g.V().Has("name","John");

Supported Databases

  • Azure Cosmos DB (Gremlin API)
  • Apache TinkerPop compatible databases
  • In-memory graph database (for testing via Stardust.Paradox.Data.InMemory)

Advanced Features

Custom Traversals with GraphContext

// Complex traversal queries using VAsync
var friendsOfFriends = await context.VAsync<IPerson>(g =>
    g.V().HasLabel("person")
     .Has("firstName", "John")
     .Out("knows")
     .Out("knows") // Friends of friends
     .Dedup()
     .Limit(10));

// Aggregation queries using ExecuteAsync
var companySizes = await context.ExecuteAsync<dynamic>(g =>
    g.V().HasLabel("company")
     .Group()
     .By("industry")
     .By(__.In("employer").Count()));

// Direct vertex queries by ID
var specificPerson = await context.VAsync<IPerson>("person-1");

// Partition key support for Cosmos DB
var personWithPK = await context.VAsync<IPerson>("person-1", "partition-key");

IGraphSet<T> Operations

// Get by ID
var person = await context.People.GetAsync("person-1");

// Get by ID with partition key (Cosmos DB)
var person = await context.People.GetAsync("person-1", "partition-key");

// Filter operations  
var techWorkers = await context.People.FilterAsync(p => p.FirstName, "John");

// Get all with pagination
var allPeople = await context.People.AllAsync();
var pagedPeople = await context.People.GetAsync(page: 0, pageSize: 20);

// Create operations
var newPerson = context.People.Create(); // Auto-generated ID
var specificPerson = context.People.Create("specific-id");
var initializedPerson = context.People.Create("id", p => {
    p.FirstName = "John";
    p.LastName = "Doe";
    return p;
});

// Delete operations
await context.People.DeleteAsync("person-1");
await context.People.DeleteAsync("person-1", "partition-key");

// Attach existing entities
context.People.Attach(existingPerson);

IEdgeGraphSet<T> Operations

// Create edges between vertices
var employment = context.Employments.Create(person, company);

// Get edge by vertices
var employment = await context.Employments.GetAsync("person-1", "company-1");

// Get edges by vertex ID
var personEmployments = await context.Employments.GetByInIdAsync("person-1");
var companyEmployments = await context.Employments.GetByOutIdAsync("company-1");

Change Tracking and Events

// Event handling
context.SavingChanges += (sender, args) => {
    Console.WriteLine($"Saving {args.TrackedItems.Count()} items");
};

context.ChangesSaved += (sender, args) => {
    Console.WriteLine($"Saved successfully. RU consumed: {context.ConsumedRU}");
};

context.SaveChangesError += (sender, args) => {
    Console.WriteLine($"Save failed: {args.Error.Message}");
    Console.WriteLine($"Failed query: {args.FailedUpdateStatement}");
};

// Automatic change tracking
var person = await context.VAsync<IPerson>("person-1");
person.FirstName = "Jane"; // Change is tracked automatically
await context.SaveChangesAsync(); // Only changed properties are updated

Dynamic Properties

// For entities implementing IDynamicGraphEntity
public interface IPerson : IVertex, IDynamicGraphEntity
{
    // ... other properties
}

// Usage
if (person is IDynamicGraphEntity dynamicPerson)
{
    dynamicPerson.SetProperty("customField", "customValue");
    dynamicPerson.SetProperty("rating", 4.5);
    
    await context.SaveChangesAsync();
    
    var customValue = dynamicPerson.GetProperty("customField");
    var propertyNames = dynamicPerson.DynamicPropertyNames;
}

Configuration Examples

Azure Cosmos DB Connection

using Stardust.Paradox.Data.Providers.Gremlin;

// Cosmos DB connection
var connector = new GremlinNetLanguageConnector(
    gremlinHostname: "your-account.gremlin.cosmos.azure.com",
    databaseName: "your-database",
    graphName: "your-collection", 
    accessKey: "your-primary-key");

Apache TinkerPop Connection

// TinkerPop Server connection
var connector = new GremlinNetLanguageConnector(
    gremlinHostname: "localhost", 
    username: "", 
    password: "",
    port: 8182,
    enableSsl: false);

In-Memory Testing

using Stardust.Paradox.Data.InMemory;
using Stardust.Paradox.Data.InMemory.Factory;

// In-memory connector for testing
var connector = InMemoryConnectorFactory.CreateForTesting();

// Or with predefined scenarios
var connector = InMemoryConnectorFactory.CreateSocialNetwork();
using var context = new MyGraphContext(connector);

// Perfect for unit testing - no external dependencies!

Testing with InMemory Provider

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Stardust.Paradox.Data.InMemory.Factory;

[TestClass]
public class GraphTests
{
    private MyGraphContext _context;
    
    [TestInitialize]
    public void Setup()
    {
        var connector = InMemoryConnectorFactory.CreateForTesting();
        _context = new MyGraphContext(connector);
    }
    
    [TestMethod]
    public async Task TestPersonCreation()
    {
        // Arrange
        var person = _context.CreateEntity<IPerson>("test-1");
        person.FirstName = "John";
        person.Adult = true;
        
        // Act
        await _context.SaveChangesAsync();
        var retrieved = await _context.VAsync<IPerson>("test-1");
        
        // Assert
        Assert.IsNotNull(retrieved);
        Assert.AreEqual("John", retrieved.FirstName);
        Assert.IsTrue(retrieved.Adult);
    }
    
    [TestMethod]
    public async Task TestRelationshipCreation()
    {
        // Arrange
        var person = _context.CreateEntity<IPerson>("person-1");
        person.FirstName = "Alice";
        
        var company = _context.CreateEntity<ICompany>("company-1");
        company.Name = "TechCorp";
        
        // Create employment edge
        var employment = _context.Employments.Create(person, company);
        employment.Manager = "Bob";
        
        // Act
        await _context.SaveChangesAsync();
        
        // Assert - Test navigation
        var employers = await person.Employers.ToVerticesAsync();
        Assert.AreEqual(1, employers.Count());
        Assert.AreEqual("TechCorp", employers.First().Name);
    }
    
    [TestCleanup]
    public void Cleanup()
    {
        _context?.Dispose();
    }
}

Performance and Monitoring

// Monitor Request Units (Cosmos DB)
Console.WriteLine($"RU consumed: {context.ConsumedRU}");

// Enable parallel save execution
GremlinContext.ParallelSaveExecution = true;

// Performance tracking with events
context.SavingChanges += (sender, args) => {
    var stopwatch = Stopwatch.StartNew();
    // Track save performance
};

Error Handling

try
{
    await context.SaveChangesAsync();
}
catch (GraphExecutionException ex)
{
    Console.WriteLine($"Save failed: {ex.Message}");
    Console.WriteLine($"Failed query: {ex.SaveEventArgs.FailedUpdateStatement}");
    
    // Inspect tracked entities that failed
    foreach (var item in ex.SaveEventArgs.TrackedItems)
    {
        Console.WriteLine($"Failed item: {item.EntityKey}");
    }
}

Advanced Configuration

Fluent Model Configuration

protected override bool InitializeModel(IGraphConfiguration configuration)
{
    configuration
        .ConfigureCollection<IPerson>()
            // Bidirectional friendship
            .Out(p => p.Friends, "knows").In<IPerson>(p => p.Friends)
            // Employment relationship  
            .Out(p => p.Employers, "employer").In<ICompany>(c => c.Employees)
            // Custom queries
            .AddQuery(p => p.Siblings, g => 
                g.V("{id}").As("s").In("parent").Out("parent").Where(p.Without("s")).Dedup())
        .ConfigureCollection<ICompany>()
            .In(c => c.Employees, "employer").Out<IPerson>(p => p.Employers)
        .ConfigureCollection<IEmployment>();
        
    return true;
}

Special Data Types

using Stardust.Paradox.Data.Annotations.DataTypes;

[VertexLabel("person")]
public interface IPerson : IVertex
{
    string Id { get; }
    string Name { get; set; }
    
    // Epoch timestamp support
    EpochDateTime CreatedAt { get; set; }
    
    // Inline serialized collections
    [InlineSerialization(SerializationType.ClearText)]
    ICollection<string> Skills { get; set; }
    
    // Complex properties
    MyComplexProperty Address { get; set; }
}

public class MyComplexProperty : IComplexProperty
{
    private string _street;
    
    public string Street
    {
        get => _street;
        set
        {
            if (value == _street) return;
            _street = value;
            OnPropertyChanged();
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    
    public void StartNotifications() { }
    public void EndNotifications() { }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  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 is compatible.  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 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on Stardust.Paradox.Data:

Package Downloads
Stardust.Paradox.Data.Providers.Gremlin

Entityframework styled tool for accessing gremlin based graph databases like CosmosDB and Apache Tinkerpop A database connector based on gremlin.net

Stardust.Paradox.Data.Providers.CosmosDb

[This package will be deprecated in V 3.0, consider moving to 'Stardust.Paradox.Data.Providers.Gremlin'] Connector for CosmosDb using the document client

Stardust.Paradox.Data.InMemory

A comprehensive, production-ready in-memory Gremlin-compatible graph database implementation for Stardust.Paradox.Data. Ideal for unit testing, integration testing, rapid prototyping, and production debugging. Features a powerful scenario framework for easy test data setup, full Gremlin query support, WebSocket and TCP endpoints, built-in Gremlin server compatible with standard Gremlin.Net clients, and seamless integration with existing Paradox applications.

Veracity.DomainEvents

Client package for consuming domain events from Veracity

Stardust.Paradox.Data.Linq

Powerful LINQ to Gremlin query provider for Stardust.Paradox graph database framework. Write natural C# LINQ queries that are automatically translated to efficient Gremlin traversals for CosmosDB, Apache TinkerPop, and other Gremlin-compatible graph databases. Features include full LINQ support (Where, Select, OrderBy, GroupBy, aggregations), type-safe graph traversals (Out, In, OutE, InE), lambda expressions, pattern matching, set operations, and async/await support. Perfect for Entity Framework-style graph database development with IntelliSense and compile-time checking.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.5.0-rc2 560 10/31/2025
2.3.20 1,137 10/8/2025
2.3.19 287 10/7/2025
2.3.18 525 9/30/2025
2.3.17 239 9/29/2025
2.3.16 1,219 11/1/2024
2.3.15 1,754 10/30/2024
2.3.14 4,189 10/20/2023
2.3.13 243 10/20/2023
2.3.10 453 9/22/2023
2.3.9 426 9/7/2023
2.3.8 3,340 2/7/2023
2.3.7 3,441 5/4/2022
2.3.6 5,966 10/12/2020
2.3.5 700 10/7/2020
Loading failed

added full support for the partitionKey when creating a vertex with GetOrCreate