neo4j-driver-dotnet-skill

Neo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery fluent API, ExecuteReadAsync/ExecuteWriteAsync managed transactions, IResultCursor (FetchAsync/ ToListAsync), record value access (.Get<T>/As<T>), null safety, UNWIND batching, temporal types, await using, EagerRe

By neo4j-contrib · 489 installs

npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill

Source repository · Upstream listing

When to Use Writing C or .NET code connecting to Neo4j Setting up IDriver , DI registration, or session/transaction lifecycle Questions about ExecutableQuery , IResultCursor , async patterns, result mapping Debugging sessions, type mapping, null safety, or error handling in .NET When NOT to Use Writing/optimizing Cypher queries → neo4j cypher skill Upgrading from older driver version → neo4j migration skill Install Package Use Neo4j.Driver Async API — use this Neo4j.Driver.Simple Synchronous wrapper Neo4j.Driver.Reactive System.Reactive streams Driver Lifecycle IDriver — thread safe, connection pooled, expensive to create. Create one per application. IDriver and IAsyncSession implement IAsyncDisposable — always await using , never plain using . Auth options: AuthTokens.Basic(u, p) / AuthTokens.Bearer(token) / AuthTokens.Kerberos(ticket) / AuthTokens.None Environment Variables Load connection config from environment / appsettings.json — never hardcode credentials. Override with environment variables (standard .NET behavior): Neo4j Uri=neo4j+s://... (double underscore = colon separator). Never commit appsettings.json with real credentials — use appsettings.Development.json (gitignored) or env vars in CI/production. DI Registration (ASP.NET Core) Register IDriver as singleton — never Scoped or Transient. Never register IAsyncSession in DI. Choose the Right API API When Auto retry Streaming driver.ExecutableQuery() Most queries — simple default ✅ ❌ eager session.ExecuteReadAsync/WriteAsync() Large results, multi query tx ✅ ✅ session.RunAsync() LOAD CSV , CALL {} IN TRANSACTIONS ❌ ✅ session.BeginTransactionAsync() Multi function, external coordination ❌ ✅ ExecutableQuery — Recommended Default Fluent builder; manages session, transaction, retries, and bookmarks automatically. Never await omitted: ExecuteAsync() returns Task — missing await compiles silently but query never runs. Never string interpolate Cypher. Always WithParameters() — prevents injection, enables plan caching. Managed Transactions Use for large result sets (lazy streaming) or multiple queries per transaction. Callback auto retried on transient failure — keep it idempotent, no side effects inside. Cursor rules: Consume with ToListAsync() or FetchAsync() loop inside the callback Returning a cursor from the callback → transaction closes → cursor invalid → exception Async void trap: FetchAsync Loop Cursor consumption methods: Method Records Summary Use ToListAsync() ✅ all ❌ Need records ToListAsync(mapper) ✅ mapped ❌ Need mapped records FetchAsync() loop ✅ one/time ❌ until ConsumeAsync Large/lazy ConsumeAsync() ❌ discards ✅ Need counters IResultSummary profiling [v6.3.0+]: summary.Profile is [Obsolete] → use summary.QueryProfile ( IQueryProfile ), whose DbHits , Rows , Time , PageCacheHits , PageCacheMisses are long? — null means not recorded, distinct from 0 . Gate on summary.HasProfile . SingleAsync() ✅ exactly 1 ❌ Expect one row Record Value Access Type Mapping Cypher .NET default Notes Integer long safe: int , long? , int? Float double safe: float , double? String string use string? if nullable Boolean bool List IReadOnlyList<object Map IReadOnlyDictionary<string,object Node INode .Labels , .Properties , .ElementId Relationship IRelationship .Type , .StartNodeElementId Date LocalDate .ToDateOnly() (.NET 6+) DateTime ZonedDateTime .ToDateTimeOffset() (ms precision) LocalDateTime LocalDateTime Duration Duration .ToTimeSpan() throws if has months/days null null use nullable types ElementId stable within one transaction only — do not use to MATCH across separate transactions. UNWIND Batching Custom class instances passed to WithParameters for UNWIND do not serialize — use new object[] { new { ... } } or Dictionary<string, object . Object Mapping (Preview API) Error Handling Catch ClientException before Neo4jException — it's a subclass; generic handler swallows it. ex.GqlStatus — stable GQL status codes; prefer over string matching ex.Code . Explicit transaction rollback can itself throw — isolate it: If CommitAsync() throws a network error, commit may or may not have succeeded — design writes idempotent with MERGE + unique constraints. Common Mistakes Mistake Fix using var driver await using var driver — IDriver is IAsyncDisposable using var session await using var session IDriver as Scoped/Transient in DI Register as Singleton IAsyncSession in DI Never — open per unit of work Missing await on ExecuteAsync() Task silently never runs async tx = tx.RunAsync(...) no inner await Remove async , return Task directly Omit database in QueryConfig / AsyncSession Always specify — saves a round trip No CancellationToken in web apps Propagate HttpContext.RequestAborted .As<string () on null graph value .As<string? () — non nullable throws record["key"] absent key Check record.Keys.Contains() first cursor.Current after FetchAsync loop Last record, not null — don't use after loop FetchAsync() after false return Throws — stop loop, don't call again Return cursor from managed tx callback Consume with ToListAsync() inside callback Need counters from session write await cursor.ConsumeAsync() AsObject<T () CS1061 compile error Add using Neo4j.Driver.Preview.Mapping; ResultAvailableAfter for total timing Use ResultConsumedAfter (full wall clock) Custom class in WithParameters for UNWIND Use anonymous types or Dictionary<string,object Rename C param but not Cypher $param Anonymous property names must match $param names ExecuteWriteAsync for reads Use ExecuteReadAsync — routes to replicas Side effects inside managed tx callback Move outside — callback retried on failure Duration.ToTimeSpan() with months/days Only safe for pure second/nanosecond durations Catch Neo4jException before ClientException ClientException is subclass — catch it first References Load on demand: [references/transactions.md](references/transactions.md) — explicit transactions, BeginTransactionAsync , rollback, commit uncertainty, TransactionConfig (timeout, metadata), causal consistency and bookmarks [references/performance.md](references/performance.md) — spatial types (Point/WGS 84/Cartesian), connection pool tuning, WithFetchSize , session config options, CancellationToken patterns, large result streaming [references/object mapping.md](references/object mapping.md) — AsObject<T , blueprint mapping, lambda mapping, AsObjectsAsync<T , repository pattern example Checklist [ ] IDriver registered as singleton in DI (or await using for short lived apps) [ ] await using on driver and sessions (not plain using ) [ ] database specified in QueryConfig / AsyncSession config [ ] ExecutableQuery used for simple queries; ExecuteReadAsync / ExecuteWriteAsync for streaming/multi query [ ] Cursor consumed inside managed tx callback (not returned) [ ] Nullable types ( string? , int? ) on any graph value that can be null [ ] WithParameters() used (no string interpolation) [ ] UNWIND batching with anonymous types (not custom class instances) [ ] CancellationToken propagated in web app handlers [ ] ClientException caught before Neo4jException [ ] Writes idempotent ( MERGE + constraints) for retry safety [ ] No side effects inside ExecuteReadAsync / ExecuteWriteAsync callbacks