MagicCSharp.Analyzers
1.0.2
dotnet add package MagicCSharp.Analyzers --version 1.0.2
NuGet\Install-Package MagicCSharp.Analyzers -Version 1.0.2
<PackageReference Include="MagicCSharp.Analyzers" Version="1.0.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="MagicCSharp.Analyzers" Version="1.0.2" />
<PackageReference Include="MagicCSharp.Analyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add MagicCSharp.Analyzers --version 1.0.2
#r "nuget: MagicCSharp.Analyzers, 1.0.2"
#:package MagicCSharp.Analyzers@1.0.2
#addin nuget:?package=MagicCSharp.Analyzers&version=1.0.2
#tool nuget:?package=MagicCSharp.Analyzers&version=1.0.2
MagicCSharp.Analyzers
The code-style conventions of a MagicCSharp codebase, as compile errors. Add it when a review comment keeps coming back — "inject the clock", "name it after its type", "that should be a record" — and you would rather the build said it, once, to everyone, before the pull request exists.
Every rule is on and an error by default, in tests too. Each is a Roslyn analyzer, so it shows in the IDE as
you type and fails dotnet build in CI; there is no separate lint step to forget.
dotnet add package MagicCSharp.Analyzers
A repository created with mcs init already references it from Directory.Build.props, so every project
gets it. The package is a development dependency: it adds nothing to your assemblies, and a project that
references yours does not inherit the rules.
The rules
| Rule | What it enforces |
|---|---|
| MCS0001 | Records declare members in a body, not a positional parameter list |
| MCS0002 | At most 4 parameters per method; group related ones into a request record (controller actions and AI tool methods marked [Description] are exempt) |
| MCS0003 | Null checks are == null / != null, not is null / is not null |
| MCS0004 | No property patterns (x is Order { Total: > 0 }); write the checks out |
| MCS0005 | No patterns that declare a variable (x is Order order); cast on its own line |
| MCS0006 | Braces on every control-flow body (else if chains and stacked usings are fine) |
| MCS0007 | A constructor or [FromServices] dependency on one of your own interfaces is named after it: IGetOrderUseCase getOrder, never useCase |
| MCS0008 | No DateTime.Now, UtcNow or Today, or DateTimeOffset.Now or UtcNow; inject TimeProvider |
| MCS0009 | Methods and local functions have block bodies; expression-bodied properties are fine |
| MCS0010 | No cryptic abbreviations (repo, ctx, svc, req, tmp, obj, …) |
| MCS0011 | Booleans start with is/has/can/should/allow/was/will/must/are |
| MCS0012 | A type's suffix matches what it implements: …UseCase for IMagicUseCase, …Repository for a repository interface |
| MCS0013 | Extra public types in a file support the one it is named for — its interface, request, result, enum, or a type it uses |
| MCS0014 | A file is named after a type it declares (Program.cs, static classes and enum-only files are exempt) |
| MCS0015 | A read-only use case (Get…, List…, Search…) does not log the Executing: trace line |
| MCS0017 | No mutable static state: static fields are readonly or const, static properties have no setter |
| MCS0018 | File-scoped namespaces |
| MCS0019 | A variable holding one of your own types, from new or foreach, is named after the type: var orderEdit = new OrderEdit() |
| MCS0020 | Infrastructure dependencies have one name each: timeProvider, keyGenService, eventDispatcher, distributedLockProvider, logger |
| MCS0021 | DbSet properties are plural |
| MCS0022 | Types ending in Request, Result, Payload or Event are records, not classes |
MCS0016 is deliberately unused, so the numbers match the rule set these came from.
"Your own" types, for MCS0007, MCS0012 and MCS0019, are the ones declared in the project being built, in
any assembly whose name starts with the same first segment — Acme.Shop.Domains.Orders and
Acme.Libraries.Events are one codebase — and in MagicCSharp's own packages. Third-party types are left
alone; you do not choose their names.
Members whose name is dictated by something else — an override, an interface implementation, a parameter of
an AI tool method — are skipped by the naming rules. To rename a property that is part of a contract, add the
new one and keep the old one marked [Obsolete], forwarding to it; obsolete members are skipped too.
Changing a rule
Every rule is configured in .editorconfig, like any other analyzer:
[*.cs]
# A warning rather than an error
dotnet_diagnostic.MCS0002.severity = warning
# Off entirely
dotnet_diagnostic.MCS0015.severity = none
# Off for one folder
[tests/**/*.cs]
dotnet_diagnostic.MCS0019.severity = none
For one line, suppress it where it happens and say why:
#pragma warning disable MCS0008 // an event records when it happened, and has no provider to ask
public DateTimeOffset OccurredOn { get; init; } = DateTimeOffset.UtcNow;
#pragma warning restore MCS0008
To take them all out of one project, drop the reference there — in a repository from mcs init, where
Directory.Build.props adds it:
<ItemGroup>
<PackageReference Remove="MagicCSharp.Analyzers" />
</ItemGroup>
Generated code
Generated code is not analyzed: anything with an <auto-generated> header, *.g.cs and *.designer.cs
files, and whatever .editorconfig marks as generated_code. An Entity Framework migration's main file has
no header, so mark migrations there — mcs init writes this for you:
[**/Migrations/*.cs]
generated_code = true
Related packages
- MagicCSharp.Cli
—
mcs initadds these rules to a new repository;mcs validatelints the conventions an analyzer cannot see - MagicCSharp — the use cases, key generation and events the naming rules know about
The whole picture, and the optional repository layout: github.com/MagicDoorInc/MagicCSharp. MIT.
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.