$npx -y skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skillNeo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery
| 1 | ## When to Use |
| 2 | - Writing C# or .NET code connecting to Neo4j |
| 3 | - Setting up `IDriver`, DI registration, or session/transaction lifecycle |
| 4 | - Questions about `ExecutableQuery`, `IResultCursor`, async patterns, result mapping |
| 5 | - Debugging sessions, type mapping, null safety, or error handling in .NET |
| 6 | |
| 7 | ## When NOT to Use |
| 8 | - **Writing/optimizing Cypher queries** → `neo4j-cypher-skill` |
| 9 | - **Upgrading from older driver version** → `neo4j-migration-skill` |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Install |
| 14 | |
| 15 | ```bash |
| 16 | dotnet add package Neo4j.Driver |
| 17 | ``` |
| 18 | |
| 19 | | Package | Use | |
| 20 | |---|---| |
| 21 | | `Neo4j.Driver` | Async API — **use this** | |
| 22 | | `Neo4j.Driver.Simple` | Synchronous wrapper | |
| 23 | | `Neo4j.Driver.Reactive` | System.Reactive streams | |
| 24 | |
| 25 | --- |
| 26 | |
| 27 | ## Driver Lifecycle |
| 28 | |
| 29 | `IDriver` — thread-safe, connection-pooled, expensive to create. **Create one per application.** |
| 30 | |
| 31 | ```csharp |
| 32 | using Neo4j.Driver; |
| 33 | |
| 34 | // URI schemes: |
| 35 | // neo4j+s://xxx.databases.neo4j.io — TLS + cluster routing (Aura) |
| 36 | // neo4j://localhost — unencrypted + cluster routing |
| 37 | // bolt+s://localhost:7687 — TLS + single instance |
| 38 | // bolt://localhost:7687 — unencrypted + single instance |
| 39 | |
| 40 | await using var driver = GraphDatabase.Driver( |
| 41 | "neo4j+s://xxx.databases.neo4j.io", |
| 42 | AuthTokens.Basic("neo4j", "password")); |
| 43 | |
| 44 | await driver.VerifyConnectivityAsync(); // fail fast on startup |
| 45 | ``` |
| 46 | |
| 47 | `IDriver` and `IAsyncSession` implement `IAsyncDisposable` — always `await using`, never plain `using`. |
| 48 | |
| 49 | ```csharp |
| 50 | // ❌ Wrong — synchronous Dispose() may block thread pool |
| 51 | using var driver = GraphDatabase.Driver(uri, auth); |
| 52 | |
| 53 | // ✅ Correct |
| 54 | await using var driver = GraphDatabase.Driver(uri, auth); |
| 55 | ``` |
| 56 | |
| 57 | Auth options: `AuthTokens.Basic(u, p)` / `AuthTokens.Bearer(token)` / `AuthTokens.Kerberos(ticket)` / `AuthTokens.None` |
| 58 | |
| 59 | --- |
| 60 | |
| 61 | ## Environment Variables |
| 62 | |
| 63 | Load connection config from environment / `appsettings.json` — never hardcode credentials. |
| 64 | |
| 65 | ```json |
| 66 | // appsettings.json |
| 67 | { |
| 68 | "Neo4j": { |
| 69 | "Uri": "neo4j+s://xxx.databases.neo4j.io", |
| 70 | "User": "neo4j", |
| 71 | "Password": "secret", |
| 72 | "Database": "neo4j" |
| 73 | } |
| 74 | } |
| 75 | ``` |
| 76 | |
| 77 | ```csharp |
| 78 | // Access via IConfiguration (injected in Program.cs) |
| 79 | var uri = builder.Configuration["Neo4j:Uri"]; |
| 80 | var user = builder.Configuration["Neo4j:User"]; |
| 81 | var password = builder.Configuration["Neo4j:Password"]; |
| 82 | var database = builder.Configuration["Neo4j:Database"] ?? "neo4j"; |
| 83 | ``` |
| 84 | |
| 85 | 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. |
| 86 | |
| 87 | --- |
| 88 | |
| 89 | ## DI Registration (ASP.NET Core) |
| 90 | |
| 91 | Register `IDriver` as **singleton** — never Scoped or Transient. Never register `IAsyncSession` in DI. |
| 92 | |
| 93 | ```csharp |
| 94 | // Program.cs |
| 95 | builder.Services.AddSingleton<IDriver>(_ => |
| 96 | GraphDatabase.Driver( |
| 97 | builder.Configuration["Neo4j:Uri"], |
| 98 | AuthTokens.Basic( |
| 99 | builder.Configuration["Neo4j:User"], |
| 100 | builder.Configuration["Neo4j:Password"]))); |
| 101 | |
| 102 | // Shutdown hook — dispose the singleton cleanly |
| 103 | builder.Services.AddHostedService<Neo4jShutdownService>(); |
| 104 | |
| 105 | // Neo4jShutdownService.cs |
| 106 | public class Neo4jShutdownService(IDriver driver, IHostApplicationLifetime lifetime) |
| 107 | : IHostedService |
| 108 | { |
| 109 | public Task StartAsync(CancellationToken _) |
| 110 | { |
| 111 | lifetime.ApplicationStopping.Register(() => |
| 112 | driver.DisposeAsync().AsTask().GetAwaiter().GetResult()); |
| 113 | return Task.CompletedTask; |
| 114 | } |
| 115 | public Task StopAsync(CancellationToken _) => Task.CompletedTask; |
| 116 | } |
| 117 | |
| 118 | // Inject into services — sessions opened per unit of work |
| 119 | public class PersonService(IDriver driver) |
| 120 | { |
| 121 | public async Task<List<string>> GetNamesAsync(CancellationToken ct = default) |
| 122 | { |
| 123 | var (records, _, _) = await driver |
| 124 | .ExecutableQuery("MATCH (p:Person) RETURN p.name AS name") |
| 125 | .WithConfig(new QueryConfig(database: "neo4j")) |
| 126 | .ExecuteAsync(ct); |
| 127 | return records.Select(r => r.Get<string>("name")).ToList(); |
| 128 | } |
| 129 | } |
| 130 | ``` |
| 131 | |
| 132 | --- |
| 133 | |
| 134 | ## Choose the Right API |
| 135 | |
| 136 | | API | When | Auto-retry | Streaming | |
| 137 | |---|---|---|- |