Migrate Dotnet8 To Dotnet9
dotnet/skills
Migrate a .NET 8 project to .NET 9 and resolve all breaking changes.
Neo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery fluent API, ExecuteReadAsync/ExecuteWriteAsync managed transactions, IResultCursor (FetchAsync/ ToListAsync)…
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skill --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/neo4j-driver-dotnet-skill .claude/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .claude/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skillType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skill --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/neo4j-driver-dotnet-skill .agents/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .agents/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skill --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/neo4j-driver-dotnet-skill .cursor/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .cursor/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/neo4j-contrib/neo4j-skills.git --path neo4j-driver-dotnet-skill--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skill --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/neo4j-driver-dotnet-skill .gemini/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .gemini/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skillInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/neo4j-driver-dotnet-skill .github/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .github/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install neo4j-contrib/neo4j-skills neo4j-driver-dotnet-skill --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/neo4j-contrib/neo4j-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/neo4j-driver-dotnet-skill .opencode/skills/neo4j-driver-dotnet-skill && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "neo4j-driver-dotnet-skill" agent skill from https://github.com/neo4j-contrib/neo4j-skills/tree/main/neo4j-driver-dotnet-skill into .opencode/skills/neo4j-driver-dotnet-skill/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "neo4j-driver-dotnet-skill", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
neo4j-driver-dotnet-skillNeo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery fluent API, ExecuteReadAsync/ExecuteWriteAsync managed transactions, IResultCursor (FetchAsync/ ToListAsync)…
Neo4j Driver Dotnet Skill is an agent skill from neo4j-contrib/neo4j-skills. 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, EagerResult, object mapping, CancellationToken, error handling, and common traps. Use when writing C or .NET code connecting to Neo4j. Also triggers on Neo4j.Driver, IDriver, ExecutableQuery, ExecuteReadAsync…
Its SKILL.md is about 4.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 5 other files, including reference files (for example `README.md`, `references/object-mapping.md` and `references/performance.md`). Compatibility notes: .NET 8, 9, 10; Neo4j.Driver v6
It sits in Development, covering Code migrations. It works with Neo4j, .NET and C#. The repository describes itself as: Neo4j Skills for Coding and other Agents including Cypher. The licence is MIT.
Read from SKILL.md and the folder at commit bb30e1f. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
BashWebFetchFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
dotnetFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
.NET 8, 9, 10; Neo4j.Driver v6
From compatibility in the SKILL.md frontmatter.
Neo4j Driver Dotnet Skill loads about 4.5k tokens when it runs, and up to ~7.2k if it reads all its reference files. Until then it costs about 185 tokens; SKILL.md has 837 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
allowed-tools: Bash, WebFetchAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from neo4j-contrib/neo4j-skills at commit bb30e1f, republished under its MIT licence (© neo4j-contrib). 837 words, ~4,509 tokens.
.claude/skills/neo4j-driver-dotnet-skill/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.IDriver, DI registration, or session/transaction lifecycleExecutableQuery, IResultCursor, async patterns, result mappingneo4j-cypher-skillneo4j-migration-skilldotnet add package Neo4j.Driver| Package | Use |
|---|---|
Neo4j.Driver | Async API — use this |
Neo4j.Driver.Simple | Synchronous wrapper |
Neo4j.Driver.Reactive | System.Reactive streams |
IDriver — thread-safe, connection-pooled, expensive to create. Create one per application.
using Neo4j.Driver;
// URI schemes:
// neo4j+s://xxx.databases.neo4j.io — TLS + cluster routing (Aura)
// neo4j://localhost — unencrypted + cluster routing
// bolt+s://localhost:7687 — TLS + single instance
// bolt://localhost:7687 — unencrypted + single instance
await using var driver = GraphDatabase.Driver(
"neo4j+s://xxx.databases.neo4j.io",
AuthTokens.Basic("neo4j", "password"));
await driver.VerifyConnectivityAsync(); // fail fast on startupIDriver and IAsyncSession implement IAsyncDisposable — always await using, never plain using.
// ❌ Wrong — synchronous Dispose() may block thread pool
using var driver = GraphDatabase.Driver(uri, auth);
// ✅ Correct
await using var driver = GraphDatabase.Driver(uri, auth);Auth options: AuthTokens.Basic(u, p) / AuthTokens.Bearer(token) / AuthTokens.Kerberos(ticket) / AuthTokens.None
Load connection config from environment / appsettings.json — never hardcode credentials.
// appsettings.json
{
"Neo4j": {
"Uri": "neo4j+s://xxx.databases.neo4j.io",
"User": "neo4j",
"Password": "secret",
"Database": "neo4j"
}
}// Access via IConfiguration (injected in Program.cs)
var uri = builder.Configuration["Neo4j:Uri"];
var user = builder.Configuration["Neo4j:User"];
var password = builder.Configuration["Neo4j:Password"];
var database = builder.Configuration["Neo4j:Database"] ?? "neo4j";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.
Register IDriver as singleton — never Scoped or Transient. Never register IAsyncSession in DI.
// Program.cs
builder.Services.AddSingleton<IDriver>(_ =>
GraphDatabase.Driver(
builder.Configuration["Neo4j:Uri"],
AuthTokens.Basic(
builder.Configuration["Neo4j:User"],
builder.Configuration["Neo4j:Password"])));
// Shutdown hook — dispose the singleton cleanly
builder.Services.AddHostedService<Neo4jShutdownService>();
// Neo4jShutdownService.cs
public class Neo4jShutdownService(IDriver driver, IHostApplicationLifetime lifetime)
: IHostedService
{
public Task StartAsync(CancellationToken _)
{
lifetime.ApplicationStopping.Register(() =>
driver.DisposeAsync().AsTask().GetAwaiter().GetResult());
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken _) => Task.CompletedTask;
}
// Inject into services — sessions opened per unit of work
public class PersonService(IDriver driver)
{
public async Task<List<string>> GetNamesAsync(CancellationToken ct = default)
{
var (records, _, _) = await driver
.ExecutableQuery("MATCH (p:Person) RETURN p.name AS name")
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync(ct);
return records.Select(r => r.Get<string>("name")).ToList();
}
}| 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 | ❌ | ✅ |
Fluent builder; manages session, transaction, retries, and bookmarks automatically.
// Read
var (records, summary, keys) = await driver
.ExecutableQuery("MATCH (p:Person {name: $name})-[:KNOWS]->(f) RETURN f.name AS name")
.WithParameters(new { name = "Alice" })
.WithConfig(new QueryConfig(
database: "neo4j",
routing: RoutingControl.Readers)) // route reads to replicas
.ExecuteAsync(cancellationToken);
foreach (var r in records)
Console.WriteLine(r.Get<string>("name"));
// Use ResultConsumedAfter for wall-clock timing (ResultAvailableAfter = time-to-first-byte only)
Console.WriteLine($"{summary.ResultConsumedAfter.TotalMilliseconds} ms");
// Write
var (_, writeSummary, _) = await driver
.ExecutableQuery("CREATE (p:Person {name: $name, age: $age})")
.WithParameters(new { name = "Bob", age = 30 })
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();
Console.WriteLine($"Created {writeSummary.Counters.NodesCreated} nodes");
// WithMap — project inline
var names = await driver
.ExecutableQuery("MATCH (p:Person) RETURN p.name AS name")
.WithConfig(new QueryConfig(database: "neo4j"))
.WithMap(r => r["name"].As<string>())
.ExecuteAsync(); // names.Result is IReadOnlyList<string>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.
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.
await using var session = driver.AsyncSession(conf => conf.WithDatabase("neo4j"));
// Read — routes to replicas
var names = await session.ExecuteReadAsync(async tx =>
{
var cursor = await tx.RunAsync(
"MATCH (p:Person) WHERE p.name STARTS WITH $prefix RETURN p.name AS name",
new { prefix = "Al" });
return await cursor.ToListAsync(r => r.Get<string>("name"));
// Consume cursor INSIDE callback — invalid after callback returns
});
// Write — void, no async needed
await session.ExecuteWriteAsync(tx =>
tx.RunAsync("MERGE (p:Person {name: $name})", new { name = "Carol" }));
// Write — async when needing counters
var summary = await session.ExecuteWriteAsync(async tx =>
{
var cursor = await tx.RunAsync(
"CREATE (p:Person {name: $name})", new { name = "Alice" });
return await cursor.ConsumeAsync(); // drains cursor, returns IResultSummary
});
Console.WriteLine($"Created {summary.Counters.NodesCreated} nodes");Cursor rules:
ToListAsync() or FetchAsync() loop inside the callback// ❌ Returns cursor — tx closes immediately after lambda returns
var cursor = await session.ExecuteReadAsync(async tx =>
await tx.RunAsync("MATCH (p:Person) RETURN p.name AS name"));
await cursor.FetchAsync(); // throws
// ✅ Consume inside
var names = await session.ExecuteReadAsync(async tx =>
{
var cursor = await tx.RunAsync("MATCH (p:Person) RETURN p.name AS name");
return await cursor.ToListAsync(r => r.Get<string>("name"));
});Async void trap:
// ❌ CS1998 warning — async with no await; RunAsync Task discarded
await session.ExecuteWriteAsync(async tx =>
tx.RunAsync("MERGE (p:Person {name: $name})", new { name = "Alice" }));
// ✅ No async, return Task directly
await session.ExecuteWriteAsync(tx =>
tx.RunAsync("MERGE (p:Person {name: $name})", new { name = "Alice" }));var cursor = await tx.RunAsync("MATCH (p:Person) RETURN p.name AS name");
while (await cursor.FetchAsync()) // true while records remain
{
Process(cursor.Current.Get<string>("name"));
}
// Do NOT use cursor.Current after the loop — it holds the last record, not null
// Do NOT call FetchAsync() again after it returned false — throws InvalidOperationExceptionCursor 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 |
// Two equivalent patterns — prefer .Get<T>()
string name = record.Get<string>("name");
int age = record.Get<int>("age");
string name2 = record["name"].As<string>(); // indexer + As<T>
string name3 = record[0].As<string>(); // by column index
// Null safety — .As<T>() on null graph value throws InvalidCastException
string? city = record["city"].As<string?>(); // ✅ nullable
int? age2 = record["age"].As<int?>(); // ✅ nullable
// Absent key — throws KeyNotFoundException (typo or not in RETURN)
if (record.Keys.Contains("city"))
var city3 = record.Get<string?>("city");| 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 |
UUID | System.Guid | [driver 6.2+, Bolt 6.1, Neo4j 2026.08+]; object mapping also converts to/from string |
null | null | use nullable types |
ElementId stable within one transaction only — do not use to MATCH across separate transactions.
// Pass CLR types as params — driver converts automatically
await driver.ExecutableQuery("CREATE (e:Event {at: $ts})")
.WithParameters(new { ts = DateTimeOffset.UtcNow })
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();// ❌ One transaction per record — high overhead
foreach (var item in items)
await driver.ExecutableQuery("MERGE (n:Node {id: $id})")
.WithParameters(new { id = item.Id })
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();
// ✅ Single transaction via UNWIND — anonymous types only (custom classes don't serialize)
var rows = items.Select(i => new { id = i.Id, name = i.Name }).ToArray();
await driver.ExecutableQuery(@"
UNWIND $rows AS row
MERGE (n:Node {id: row.id})
SET n.name = row.name")
.WithParameters(new { rows })
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();Custom class instances passed to WithParameters for UNWIND do not serialize — use new object[] { new { ... } } or Dictionary<string, object>.
using Neo4j.Driver.Preview.Mapping; // REQUIRED — without this, AsObject<T>() is CS1061
public record Person(string Name, int Age); // C# records work well here
var result = await driver
.ExecutableQuery("MATCH (p:Person) RETURN p.name AS name, p.age AS age")
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();
var person = result.Result[0].AsObject<Person>(); // RETURN keys map to record properties
// Bulk mapping
var (people, _, _) = await driver
.ExecutableQuery("MATCH (p:Person) RETURN p.name AS name, p.age AS age")
.WithConfig(new QueryConfig(database: "neo4j"))
.AsObjectsAsync<Person>();try
{
await driver.ExecutableQuery("...")
.WithConfig(new QueryConfig(database: "neo4j"))
.ExecuteAsync();
}
catch (AuthenticationException ex) { /* bad credentials */ }
catch (ServiceUnavailableException ex) { /* database unreachable */ }
catch (ClientException ex)
when (ex.Code == "Neo.ClientError.Schema.ConstraintValidationFailed")
{
// Unique/existence constraint violation — catch BEFORE Neo4jException
}
catch (Neo4jException ex) { /* all other server errors */ }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:
catch (Exception original)
{
try { await tx.RollbackAsync(); }
catch (Exception ex) { logger.LogError(ex, "Rollback failed"); }
throw;
}If CommitAsync() throws a network error, commit may or may not have succeeded — design writes idempotent with MERGE + unique constraints.
| 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 |
Load on demand:
BeginTransactionAsync, rollback, commit uncertainty, TransactionConfig (timeout, metadata), causal consistency and bookmarksWithFetchSize, session config options, CancellationToken patterns, large result streamingAsObject<T>, blueprint mapping, lambda mapping, AsObjectsAsync<T>, repository pattern exampleIDriver 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 configExecutableQuery used for simple queries; ExecuteReadAsync/ExecuteWriteAsync for streaming/multi-querystring?, int?) on any graph value that can be nullWithParameters() used (no string interpolation)CancellationToken propagated in web app handlersClientException caught before Neo4jExceptionMERGE + constraints) for retry safetyExecuteReadAsync/ExecuteWriteAsync callbacks© neo4j-contrib, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 4 other files (references) in neo4j-driver-dotnet-skill of neo4j-contrib/neo4j-skills.
Open the folder on GitHubat commit bb30e1f
Neo4j Driver Dotnet Skill next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Neo4j Driver Dotnet Skill this skillneo4j-contrib/neo4j-skills | 114 | — | ~4.5k | Automated safety check: Notes | MIT | |
| Migrate Dotnet8 To Dotnet9dotnet/skills | 5.6k | 2 repos | ~3.9k | Automated safety check: Pass | MIT | |
| Csharp Expertmicrosoft/testfx | 1k | 2 repos | ~5.4k | Automated safety check: Pass | MIT | |
| Migrate Dotnet9 To Dotnet10dotnet/skills | 5.6k | 2 repos | ~4.8k | Automated safety check: Pass | MIT | |
| Migrate Dotnet10 To Dotnet11dotnet/skills | 5.6k | 1 repos | ~3.6k | Automated safety check: Pass | MIT | |
| Speckit ConstitutionWeihanLi/WeihanLi.Common | 242 | 12 repos | ~2.1k | Automated safety check: Pass | Apache-2.0 |
dotnet/skills
Migrate a .NET 8 project to .NET 9 and resolve all breaking changes.
microsoft/testfx
Route C and .NET requests to the exact installed specialist or smallest dotnet/skills marketplace plugin.
dotnet/skills
Migrate a .NET 9 project or solution to .NET 10 and resolve all breaking changes.
dotnet/skills
Migrate a .NET 10 project or solution to .NET 11 and resolve all breaking changes.
WeihanLi/WeihanLi.Common
Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
neo4j-contrib/neo4j-skills
Manages Neo4j Aura Agents via the v2beta1 REST API — create, list, get, update, delete, and invoke Aura agents backed by an AuraDB instance.
neo4j-contrib/neo4j-skills
Generates, optimizes, and validates Cypher 25 queries for Neo4j 2025.x and 2026.x.
neo4j-contrib/neo4j-skills
Serverless Aura Graph Analytics (AGA) GDS Sessions — covers GdsSessions, AuraGraphDataScience, AuraAPICredentials, DbmsConnectionInfo, SessionMemory, getorcreate, remote graph projection with…
neo4j-contrib/neo4j-skills
Orchestrates zero-to-running-app in 8 stages — prerequisites → context → provision → model → load → explore → query → build.
neo4j-contrib/neo4j-skills
Provisions and manages Neo4j Aura instances via CLI (aura-cli v1.7+) or REST API.
neo4j-contrib/neo4j-skills
Covers the Neo4j Go Driver v6 — driver lifecycle, ExecuteQuery, managed and explicit transactions, session config, error handling, data type mapping, and connection tuning.
Categories
Neo4j .NET Driver v6 — IDriver lifecycle, DI registration (singleton), ExecutableQuery fluent API, ExecuteReadAsync/ExecuteWriteAsync managed transactions, IResultCursor (FetchAsync/ ToListAsync)…. Neo4j Driver Dotnet Skill is an agent skill from neo4j-contrib/neo4j-skills.Get<T/As<T), null safety, UNWIND batching, temporal types, await using, EagerResult, object mapping, CancellationToken, error handling, and common traps.
Neo4j Driver Dotnet Skill fits situations like: .NET code connecting to Neo4j; executableQuery; executeReadAsync; executeWriteAsync.
Run `npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a claude-code`. Or copy the skill folder (neo4j-driver-dotnet-skill in neo4j-contrib/neo4j-skills) into .claude/skills/neo4j-driver-dotnet-skill in your project. Claude Code loads it when a task matches its description.
Run `npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a codex`. Or copy the skill folder (neo4j-driver-dotnet-skill in neo4j-contrib/neo4j-skills) into .agents/skills/neo4j-driver-dotnet-skill in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-dotnet-skill -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/neo4j-driver-dotnet-skill, .gemini/skills/neo4j-driver-dotnet-skill, .github/skills/neo4j-driver-dotnet-skill and .opencode/skills/neo4j-driver-dotnet-skill in your project.
Going by SKILL.md and its folder, Neo4j Driver Dotnet Skill needs the command-line tools its instructions call (dotnet). Its frontmatter pre-approves these tools: Bash, WebFetch. Compatibility (from SKILL.md): .NET 8, 9, 10; Neo4j.Driver v6.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Neo4j Driver Dotnet Skill is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.5k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 2.7k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Neo4j Driver Dotnet Skill: Migrate Dotnet8 To Dotnet9 (dotnet/skills, 5.6k stars), Csharp Expert (microsoft/testfx, 1k stars), Migrate Dotnet9 To Dotnet10 (dotnet/skills, 5.6k stars) and Migrate Dotnet10 To Dotnet11 (dotnet/skills, 5.6k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
neo4j-contrib (a GitHub organization) maintains it in neo4j-contrib/neo4j-skills, which has 114 GitHub stars. The repository holds 28 skills in this directory. The repository was last updated on October 9, 2026.
Source: neo4j-contrib/neo4j-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.