Languages / C#

C# and .NET code analysis: unused code, call graphs and complexity

Roslyn analyzers (IDE0051, IDE0052) and Rider or ReSharper find unused private members inside a .NET solution. repowise finds unused types and files across the solution: it reads every .csproj and .sln, follows ProjectReferences and global usings, treats ASP.NET controllers, minimal API handlers and assembly-scanned handlers as live, and ranks each finding by git history.

Last reviewed: November 2026

Full tier

What repowise detects in C#

Checked against the engine source. Every row runs locally with zero LLM calls.

repowise capabilities for C#
Files parsed.cs, plus Razor and Blazor components projected to C#
Import resolutionFull. Namespace map from every .csproj and .sln, implicit and global usings, ProjectReference order, NuGet PackageReference nodes
Call graphResolved calls, inheritance and interface implementation
Dead codeName-granular reachability through the project graph; unreachable files and unused types
Health markersComplexity, class metrics, Extract Method plans
Performance markersEF Core, Dapper and ADO.NET queries inside foreach, ToListAsync and peers in loops, .Result and .Wait() inside async methods
Coverage reportsCobertura from coverlet, lcov, Clover
Frameworks recognisedASP.NET controllers and MapGet handlers, DbContext to entity files, EF Core configurations, FastEndpoints, MediatR, MassTransit and NServiceBus handlers, Azure Functions, BenchmarkDotNet, source generators

Specific to this language

How C# code is read

The solution is the unit

repowise parses every .csproj and .sln into a namespace-to-file map and resolves a using in the same project first, then in ProjectReference'd projects. Implicit and global usings are collected per project. Unresolved namespaces that match a PackageReference become NuGet nodes instead of guesses.

Assembly scanning is modelled

Types a framework discovers by scanning the assembly are linked only when the base type is declared and a registration call exists: EF Core ApplyConfigurationsFromAssembly, FastEndpoints, MediatR handlers, MassTransit consumers and NServiceBus handlers. Azure Functions, WCF service contracts, BenchmarkDotNet benchmarks and source generators are roots.

Rescue only, never accuse

C# reachability only rescues files: a file is live when a type it declares is named in a file that can see it through the project graph, extension methods count as use, and assembly attributes or global using keep a file live. A name in a string counts; a name in a comment does not.

Honest comparison

Native C# tools, and when they are enough

Native C# analysis tools
ToolWhat it covers
Roslyn analyzers (IDE0051, IDE0052, IDE0060)Unused private members and parameters, with code fixes in Visual Studio and dotnet build.
ReSharper and RiderSolution-wide analysis that finds unused public types and members, in the IDE.
Visual Studio code metricsCyclomatic complexity, maintainability index, class coupling per method and type.
NDependDependency graphs, rules written as queries, technical-debt estimates for .NET.
coverletCoverage for dotnet test; writes Cobertura, which repowise reads.

Where repowise adds value

  • Headless, repository-wide dead code with confidence and owners, in CI or for an agent, without opening the IDE.
  • Framework roots for ASP.NET, EF Core and assembly-scanned handlers, so they are not reported.
  • Git history: hotspots, ownership and co-change across the solution.
  • One graph with the TypeScript frontend or Python services in the same repository.

How to run it

Analyse a C# repository

From the repository root. Agents get the same results through the get_dead_code, get_health and get_context MCP tools.

uv tool install repowise        # or: pipx install repowise
repowise init --yes --no-prose     # index the repo, no API key
repowise dead-code --min-confidence 0.7 --safe-only
repowise health --refactoring-targets
dotnet test --collect:"XPlat Code Coverage"
repowise coverage add TestResults/<guid>/coverage.cobertura.xml
repowise serve                     # graph, health and dead code in a local dashboard

Questions

What people ask before they try it.

How do I find unused code in a C# solution?

Inside the IDE, Rider or ReSharper solution-wide analysis is exact. For a headless report across the whole repository, run repowise dead-code: it reads .csproj and .sln files, follows ProjectReferences and reports unreachable files and unused types with a confidence and the primary owner.

Will ASP.NET controllers be reported as unused?

No. Classes marked [ApiController] or named *Controller and anchored by MapControllers, minimal API MapGet handlers and DbContext entity sets are linked as framework edges.

Does it read coverlet output?

Yes. dotnet test with the XPlat Code Coverage collector writes coverage.cobertura.xml; repowise coverage add reads it.

Does repowise send my C# code to an LLM?

No. Parsing, the call graph, dead code, health scores, performance markers and git history all run locally with zero LLM calls. repowise init --yes --no-prose needs no API key, and the analysis itself makes no network calls. Written documentation prose is the one opt-in step that uses a model, and it can run on a local Ollama model.

Index a C# repository.

Public repositories are free on repowise.dev. The CLI is open source and runs on your machine with no API key.