Skip to main content

Fixed: "error CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array" in an EF Core Migration

· 9 min read
Jagdish Kumawat
Founder @ Dewiride

The second migration I added to a .NET 10 project stopped the build with error CA1861. EF Core writes the columns of a two-column index as a constant array, the code analyzer asks for that array to be a static readonly field, and the project treats warnings as errors. The rule's own page says it's safe to suppress when the code runs once, which is what a migration does, so the fix is one setting that turns CA1861 off for migration files only.

What I Was Doing​

I was adding the second migration to the solution in How to Connect a .NET C# Web API to a MySQL Database — Part 2, with .NET SDK 10.0.403 and EF Core 10.0.14. Its Directory.Build.props applies strict rules to every project:

Directory.Build.props
<Project>
<!-- Build settings shared by every project in this folder and the folders below it. -->
<PropertyGroup>
<!-- Stop the build on any compiler, analyzer or NuGet warning, so problems get fixed early. -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Turn on the recommended set of .NET code-quality rules for the newest SDK. -->
<AnalysisLevel>latest-recommended</AnalysisLevel>
<!-- Check the code-style rules in .editorconfig during the build, not only in the editor. -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>

The new table, stock_movements, has an index on two columns, so that a product's history can be read quickly:

src/Dewiride.Inventory.Data/Configurations/StockMovementConfiguration.cs (excerpt)
builder.HasIndex(m => new { m.ProductId, m.OccurredAtUtc });

dotnet ef migrations add AddStockMovements created the migration without complaint. Then I built the solution:

Terminal
dotnet build

What I Expected​

Build succeeded. The first migration, CreateCatalogTables, had built cleanly a few minutes earlier, and I hadn't touched the new migration file.

What I Got​

Output
Restore complete (0.5s)
Dewiride.Inventory.Data net10.0 failed with 1 error(s) (0.2s)
/Users/you/projects/dewiride-inventory-api/src/Dewiride.Inventory.Data/Migrations/20261103031745_AddStockMovements.cs(44,26): error CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)

Build failed with 1 error(s) in 0.8s

A terminal where dotnet build fails for Dewiride.Inventory.Data with error CA1861 at line 44, column 26 of the AddStockMovements migration: Prefer 'static readonly' fields over constant array arguments.

What I Checked​

AddStockMovements.cs(44,26) means line 44, column 26 of the migration EF Core had just written. That's the call that creates the index:

src/Dewiride.Inventory.Data/Migrations/20261103031745_AddStockMovements.cs (lines 41–44)
migrationBuilder.CreateIndex(
name: "IX_stock_movements_ProductId_OccurredAtUtc",
table: "stock_movements",
columns: new[] { "ProductId", "OccurredAtUtc" });

Column 26 is new[]: an array of two fixed strings, created as an argument to CreateIndex. The first migration only had one-column indexes, which EF Core writes with column: "Name", a single string. That's why it built.

Next question: why does the analyzer check this file at all? The same migration has a .Designer.cs file, and the folder has a model snapshot, and both are full of generated code without any errors. The configuration options page answers it: "files with certain file extensions or auto-generated file headers are treated as generated code files. For example, a file name ending with .designer.cs or .generated.cs is considered generated code." The designer file and the snapshot both start with // <auto-generated />. The migration file doesn't, on purpose: EF Core's Managing Migrations page says "you should always review the code and make sure it corresponds to the desired change; in some cases, it is even necessary to do so." It's a file you're expected to read and sometimes edit, so the analyzers treat it as your code.

Last, why is it an error? The CA1861 page lists the rule as "Enabled by default in .NET 10: As suggestion". AnalysisLevel set to latest-recommended raises it to a warning, and TreatWarningsAsErrors turns that warning into an error.

Why It Happens​

The rule's page explains the advice: "Constant arrays passed as arguments are not reused when called repeatedly, which implies a new array is created each time." In a method that runs thousands of times, moving the array into a static readonly field saves those allocations.

A migration isn't that kind of method. Its Up method runs once for each database, when the migration is applied. The same page lists when to suppress the rule, and the first case is: "The invocation only runs once."

The Fix​

The page also says how: "To disable the rule for a file, folder, or project, set its severity to none in the configuration file." I turned it off for migration files only, by adding a section to the end of the solution's .editorconfig:

.editorconfig (added at the end)
# EF Core migrations
[**/Migrations/*.cs]

# A migration runs once per database, so CA1861's advice to reuse arrays across repeated
# calls doesn't apply to the arrays EF Core writes into migrations.
dotnet_diagnostic.CA1861.severity = none

[**/Migrations/*.cs] selects every .cs file in any folder named Migrations, at any depth. The rule stays on everywhere else, which I checked before trusting it: in a copy of the solution, I added a throwaway class that passes new[] { "a", "b" } to string.Join, and CA1861 still failed that build.

I didn't use the other options, for these reasons:

OptionWhy not
Move the array into a static readonly field in the migrationIt works, but every future migration with a multi-column index needs the same hand edit
generated_code = true for the Migrations folderIt hides every analyzer rule from files you may edit by hand
#pragma warning disable CA1861 in the migrationSame problem as editing the array: one edit per migration
Turning off TreatWarningsAsErrors or CA1861 for the whole projectYour own code loses the rule too

After the Fix​

Terminal
dotnet build
Output
Restore complete (0.5s)
Dewiride.Inventory.Data net10.0 succeeded (0.2s) → src/Dewiride.Inventory.Data/bin/Debug/net10.0/Dewiride.Inventory.Data.dll
Dewiride.Inventory.Api net10.0 succeeded (0.4s) → src/Dewiride.Inventory.Api/bin/Debug/net10.0/Dewiride.Inventory.Api.dll

Build succeeded in 1.1s

A terminal where dotnet build now succeeds for Dewiride.Inventory.Data and Dewiride.Inventory.Api and prints Build succeeded in 1.1s.

The migration is exactly as EF Core wrote it, and later migrations with multi-column indexes build too. A third migration in the same project, with two more of these arrays, built without any change.

Common Mistakes​

MistakeWhat happensDo this instead
Editing the generated migration to silence the errorEvery future migration needs the same editTurn the rule off for the Migrations folder
Turning CA1861 off in the [*.cs] sectionThe rule stops checking your own codeUse a section that matches only migration files
Adding the section above the [*.cs] sectionA later section that sets the same rule winsAdd it at the end of .editorconfig
Removing TreatWarningsAsErrorsNew warnings scroll past unnoticedKeep it, and suppress only what the rule's page allows

Troubleshooting​

SymptomLikely causeFix
CA1861 still fails the build after the changeThe section's pattern doesn't match the file, or another .editorconfig closer to the file sets the rule againCheck the folder name is exactly Migrations and look for other .editorconfig files
CA1861 points at .Designer.cs or the snapshotNot expected: those files are treated as generated codeCheck the file still starts with // <auto-generated />
Your migrations live in a folder with another namedotnet ef migrations add --output-dir was usedChange the section to match that folder

FAQ​

Is it safe to suppress CA1861 for migrations?​

Yes. The rule's page lists "The invocation only runs once" as a reason to suppress it, and a migration's Up method runs once per database.

Will EF Core stop writing arrays?​

The generated code is EF Core's choice, and EF Core 10.0.14 writes new[] { ... } for multi-column indexes. The setting keeps working whatever later versions do.

Does this hide other problems in migrations?​

No. Only CA1861 is off for those files. Every other rule still checks them, which matters because you sometimes edit migrations by hand.

Conclusion​

error CA1861 in an EF Core migration comes from the constant array EF Core writes for a multi-column index, checked by the recommended rules and turned into an error by TreatWarningsAsErrors. A migration runs once per database, which the rule's page names as a reason to suppress it, so set dotnet_diagnostic.CA1861.severity = none in an .editorconfig section for **/Migrations/*.cs and keep the rule everywhere else.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.