Skip to content

.NET Framework and "Any CPU" Builds

When you build a .NET Framework project (targeting net471 or later) that references ThinkGeo as Any CPU, the build may fail with:

ThinkGeo.Core: .NET Framework projects cannot build as 'Any CPU' because the native SQLite engine (e_sqlite3) is architecture-specific.

On older builds the same situation produced a terser message from the underlying package that read simply This package does not support Any CPU builds.

This page explains why, and how to fix it.

Why this happens

ThinkGeo.Core uses SQLite (via Microsoft.Data.Sqlite / SQLitePCLRaw) for several data sources, such as MBTiles layers and SQLite feature sources. SQLite ships as a native library (e_sqlite3.dll) that is architecture-specific — a process is either x86 or x64, and it must load the matching native.

Starting with SQLitePCLRaw 3.x — which ThinkGeo adopted to pick up a security-patched SQLite (CVE-2025-6965) — the native package no longer supports the old "ship every architecture and pick one at runtime" behavior on classic .NET Framework, and fails the build instead. This is not specific to ThinkGeo: any project that moves the SQLitePCLRaw native from 2.x to 3.x runs into the same limitation.

Modern .NET is not affected. On .NET 6 / 8 / 10 (including MAUI, Blazor, Android, and iOS) the runtime resolves the correct native automatically, so Any CPU works. The limitation applies only to classic .NET Framework projects built as Any CPU — most commonly class libraries.

How to fix it

Build your .NET Framework project for a specific architecture. Either option works.

In Visual Studio, open Build → Configuration Manager and set the active solution platform to x64 (or x86) instead of Any CPU. From the command line, pass -p:Platform=x64.

<PlatformTarget>x64</PlatformTarget> by itself is not enough. The check looks at the build Platform, so it must be an x64/x86 build configuration (or a RuntimeIdentifier — see below).

Option 2 — Inject a RuntimeIdentifier for .NET Framework targets

If you want to keep an "Any CPU" solution configuration, set a RuntimeIdentifier for your .NET Framework targets only. A convenient place is a Directory.Build.props at the root of your solution:

<Project>
  <!-- Give .NET Framework "Any CPU" builds a RuntimeIdentifier so the native SQLite resolves. -->
  <PropertyGroup Condition="'$(RuntimeIdentifier)' == ''
                            and $([System.String]::Copy('$(Platform)').Replace(' ', '').ToLower()) == 'anycpu'
                            and '$(TargetFramework)' != ''
                            and $([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>
</Project>

Use win-x86 instead of win-x64 if the app must run as 32-bit (for example, to load 32-bit COM components). Setting a RuntimeIdentifier makes that build architecture-specific, which is expected.

Which projects are affected

Project Affected?
.NET Framework 4.7.1+ built as Any CPU (class libraries especially) Yes — build fails
.NET Framework built as x64 or x86 No
.NET 6 / 8 / 10 (console, ASP.NET Core, WinForms/WPF on modern .NET) No
MAUI, Blazor, Android, iOS No

Background

Native code has a fixed architecture, but an "Any CPU" managed assembly does not — it runs as x86 or x64 depending on the host process. Making that combination work requires selecting the matching native at run time. Modern .NET does this automatically through the runtime's RID-based native asset resolution (deps.json); classic .NET Framework has no equivalent facility, so the native SQLite package now asks you to commit to an architecture up front.