Stardust.Paradox.Data
2.3.20
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
<PackageReference Include="Stardust.Paradox.Data" Version="2.3.20" />
<PackageVersion Include="Stardust.Paradox.Data" Version="2.3.20" />
<PackageReference Include="Stardust.Paradox.Data" />
paket add Stardust.Paradox.Data --version 2.3.20
#r "nuget: Stardust.Paradox.Data, 2.3.20"
#:package Stardust.Paradox.Data@2.3.20
#addin nuget:?package=Stardust.Paradox.Data&version=2.3.20
#tool nuget:?package=Stardust.Paradox.Data&version=2.3.20
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 | Versions 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. |
-
.NETStandard 2.0
- Microsoft.CSharp (>= 4.7.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.1.1)
- Newtonsoft.Json (>= 13.0.1)
- Stardust.Paradox.Data.Annotations (>= 2.3.3)
- Stardust.Particles (>= 5.0.2)
- System.IO.Compression (>= 4.3.0)
- System.Reflection (>= 4.3.0)
- System.Reflection.Emit (>= 4.7.0)
- System.Reflection.Emit.ILGeneration (>= 4.7.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- System.Reflection.Primitives (>= 4.3.0)
- System.Reflection.TypeExtensions (>= 4.7.0)
-
net6.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.1)
- Stardust.Paradox.Data.Annotations (>= 2.3.3)
- Stardust.Particles (>= 5.0.2)
-
net7.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.1)
- Stardust.Paradox.Data.Annotations (>= 2.3.3)
- Stardust.Particles (>= 5.0.2)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.1)
- Stardust.Paradox.Data.Annotations (>= 2.3.3)
- Stardust.Particles (>= 5.0.2)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.1)
- Stardust.Paradox.Data.Annotations (>= 2.3.3)
- Stardust.Particles (>= 5.0.2)
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 |
added full support for the partitionKey when creating a vertex with GetOrCreate