csharp-scripts
Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file…
Optimize and improve the performance of slow Entity Framework Core (EF Core) queries: make them generate less SQL, make fewer database round-trips, and return results faster. Use whenever an EF Core or DbContext query or data-access path is slow or should be made faster —
$ npx -y skills add dotnet/skills --skill optimizing-ef-core-queries --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/optimizing-ef-core-queriesContext preview
The summary Claude sees to decide when to auto-load this skill.
Optimize and improve the performance of slow Entity Framework Core (EF Core) queries: make them generate less SQL, make fewer database round-trips, and return results faster. Use whenever an EF Core or DbContext query or data-access path is slow or should be made faster —
name: optimizing-ef-core-queries description: "Optimize and improve the performance of slow Entity Framework Core (EF Core) queries: make them generate less SQL, make fewer database round-trips, and return results faster. Use whenever an EF Core or DbContext query or data-access path is slow or should be made faster — whether or not EF Core owns the database schema. For EF Core, not Dapper or raw ADO.NET." license: MIT
Diagnose and fix slow Entity Framework Core (EF Core) queries. Start from the generated SQL/logs, apply the smallest change that removes the bottleneck, and confirm the fix by re-reading the SQL and the query count. Prefer changes that reduce round-trips, duplicated rows, scans, or per-call translation cost over micro-optimizations. Apply one change at a time and re-measure.
You cannot optimize what you cannot see. Turn on command logging and read the SQL and query count before changing anything:
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information); // or set "Microsoft.EntityFrameworkCore.Database.Command": "Information" in appsettings.json
Tag a query with `.TagWith("...")` to find it in the log. Count how many statements a slow operation runs, and how many rows each returns, before and after each change.
An index can only be used when the indexed column appears **bare** on one side of the comparison. Wrapping it in a function or arithmetic — `CreatedAt.Year == y`, `CreatedAt.Date == d`, `ToLower(Name) == n`, `Price * 1.1 > x`, or a leading-wildcard `LIKE '%foo'` — forces a per-row computation the index cannot satisfy, so the query **scans the whole table even though the index exists**. Adding another index changes nothing. Rewrite the predicate so the column stays bare, usually as a half-open range:
// Non-sargable: a function is computed for every row → full scan db.Logs.Where(l => l.CreatedAt.Year == year); // Sargable: bare column compared to constants → index seek var start = new DateTime(year, 1, 1); db.Logs.Where(l => l.CreatedAt >= start && l.CreatedAt < start.AddYears(1));
The same rule covers several common shapes:
**Verify:** the plan shows a seek/index instead of a scan and duration drops. If the column genuinely has no index, add one (see below) — but only after the predicate is sargable.
On a very hot path that runs the *same* query shape thousands of times over a reused context, EF Core re-parses the LINQ expression tree and probes its query cache on every call. When the query is already minimal (an indexed lookup or a small projection) and read-only tweaks such as `AsNoTracking` buy nothing, that per-call translation is the remaining cost. Compile the query once with `EF.CompileQuery` / `EF.CompileAsyncQuery` and reuse the delegate:
private static readonly Func<AppDbContext, int, ProductListItem> GetProduct =
EF.CompileQuery((AppDbContext db, int id) =>
db.Products.Where(p => p.Id == id)
.Select(p => new ProductListItem(p.Id, p.Name, p.Price))
.First());
public ProductListItem Lookup(AppDbContext db, int id) => GetProduct(db, id);The delegate is `static` (compiled once) and takes the `DbContext` plus each parameter as arguments. Use it for endpoints or loops that execute one query shape at very high frequency; it does nothing for one-off queries.
**Verify:** the hot loop's mean time drops with identical results.
The same `SELECT` repeated once per row (a navigation accessed inside a loop) is an N+1. Load the related data in one round-trip — project the aggregates with `Select`, or eager-load with `Include`:
var summaries = await db.Orders
.Select(o => new OrderSummary(o.Id, o.Items.Count, o.Items.Sum(i => i.Price)))
.ToListAsync();Prefer projection or `Include` over lazy loading: lazy loading is a leading cause of N+1 and forces synchronous I/O. In server apps, don't enable `Microsoft.EntityFrameworkCore.Proxies` or mark navigations `virtual` for lazy loading.
**Ver
This repository contains the .NET team's curated set of portable skills and host-specific custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io.
Repo: dotnet/skills
Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file…
Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime,…
Set up NuGet trusted publishing (OIDC) on a GitHub Actions repo — replaces long-lived API keys with short-lived tokens. USE FOR: trusted publishing, NuGet…
Design, implement, optimize, and review SIMD code in .NET. USE FOR: vectorizing scalar loops with TensorPrimitives, Vector64/128/256/512, or platform hardware…
Guides technology selection and implementation of AI and ML features in .NET 8+ applications using ML.NET, Microsoft.Extensions.AI (MEAI), Microsoft Agent…
Configure OpenTelemetry distributed tracing, metrics, and logging in ASP.NET Core using the .NET OpenTelemetry SDK. Use when adding observability, setting up…