For affected write paths, require the intended value to survive both a new DbContext and a raw database read on the exact release candidate. Keep the rollout blocked if either check fails; choose a workaround only after it passes the same contract.
The release decision
A team can show a successful save and a correct object in memory while the database still lacks the intended value. For a VP of Engineering or Technology approving an EF Core upgrade, that creates a specific acceptance problem: the demonstration can pass without proving that the write survived.
The case here concerns optional owned objects in a particular inheritance mapping. An absent object and an object containing zero or false can mean different things to an application. If that distinction matters to the workflow, losing it can change what a later request sees. The business consequence depends on your model; this experiment measures no customer loss or production incident.
Ask the team to identify whether the mapping and write transition exist in the application. Where they do, make fresh-context and raw-column assertions a release condition. This is a focused check of affected workflows, not evidence that every EF Core application is unsafe.
The evidence to request before approval
Ask for one small, repeatable test per affected write path, with the deployed mapping and intended values stated explicitly. The acceptance record should contain the following evidence.
- The exact SDK, runtime, EF provider, dependency lock and database version used by the release candidate. A linked issue or a package-family name does not identify what was tested.
- A row that begins with an absent owned object and actual NULL owned columns, followed by the application’s intended write. Include all-default, mixed default/non-default, and fully non-default values.
- The generated SQL, the exact raw values after save, and the materialized object from a new DbContext after disposing the write context. Require both object presence and the intended values.
- A control that would expose an oversimplified test. In this example, removing shared column names makes the cases pass because it changes the schema. Record that difference instead of treating the simplified model as proof.
Choose the next change from the failed contract
If the faithful test fails, hold the affected write path from the rollout and ask for a bounded remedy. The local evidence supports three options to evaluate.
An explicit state change is the smallest code change tested here: discover the new owned entry and mark that entry Modified. All eight shared-column absent-to-default cases passed on each tested EF 10 package. This is evidence for this synthetic mapping. A real application must retest its converters, concurrency rules and update path before adopting it.
A mapping change is a larger option. The separate-column control passed all 56 cases per variant, but it created four owned columns instead of two. A production change therefore needs a schema migration, data movement and compatibility review. Removing HasColumnName from a test is not a workaround for a deployed shared-column schema.
Waiting for an appropriate stable fix is another choice if the schedule permits. Recheck the exact shipped package and run the contract again. The passing EF 11 prerelease below is useful comparison evidence; it is not a recommendation to move production to a prerelease.
Do not substitute a non-default business value merely to force a write. That changes the meaning of the record. If a proposed remedy leaves any required column NULL or the owned object absent after reload, reject it even when SaveChanges returns a positive count.
The mapping the technical test preserves
The executable probe uses two concrete child types in one table through table-per-hierarchy inheritance. Each child has an optional OwnsOne value object. Both map that object’s members to the same physical column names. The enum is converted to a string; the matrix covers both native and string-converted booleans. Nullable reference types are disabled to match the report; the experiment does not establish that disabling them is necessary.
Each case uses a new SQLite database. The writer is disposed before verification through a fresh DbContext, and a Microsoft.Data.Sqlite command reads every physical column without EF materialization. The connection stays open to preserve the in-memory SQLite database. This is the relational SQLite provider, not the EF InMemory provider.
The companion runs seven transitions across shared/separate columns, two bool mappings, two child types and synchronous/asynchronous saves: 112 cases per package/runtime combination. Besides writes from absence, it checks both mutation and replacement of an existing non-default owned object.
A positive save count still missed the intended object
On EF Core SQLite 10.0.12, both the tracked object and a tracking query in the same context showed the intended values after save. The raw row and fresh-context result disagreed. The representative string-converted cases below start with both owned columns NULL.
The all-default write returned 0. The mixed Pass/false write returned 1 but updated only the enum column. In both cases the new context materialized no owned object. Checking that at least one entry saved would miss the second failure.
The same failure pattern appeared for Unknown/true, leaving the enum column NULL. Fully non-default writes passed, as did changing an existing non-default object to defaults. A happy-path test using only populated records would therefore miss the transition that matters.
Intended owned value SaveChanges Stored enum / bool Fresh context Unknown / false 0 NULL / NULL Object absent Pass / false 1 Pass / NULL Object absent Pass / true 1 Pass / "1" Object present Tracked values were correct in all three rows. The bool converter stores "0" or "1" as strings.
Match the fix to the package being shipped
As checked on October 4, 2026, the NuGet package listing showed 10.0.12 as the newest stable EF Core SQLite package and 11.0.0-rc.1.26425.128 as the newest prerelease. The measured matrix is below. A failure means the intended raw values or fresh-context object did not survive.
Issue #37525 is closed with an EF 11 milestone, and PR #37751 merged the shared-column update fix. The maintainer discussion chose EF 11. The issue’s closed state does not establish that the fix is in EF 10.0.12; that stable package still failed this probe.
Retargeting to .NET 11 RC while keeping EF 10.0.12 still failed 24 cases. The retargeted control changes the SDK and target framework as well as the runtime. It still fails with EF 10.0.12; the passing EF 11 comparison changes the EF/provider package stack, consistent with the merged relational update change. EF 11 also changes dependencies, including native SQLite, so this is not an isolated test of a single patched binary.
EF Core SQLite package Runtime Passed Failed 10.0.2 (historical) .NET 10.0.12 88 24 10.0.12 .NET 10.0.12 88 24 10.0.12 .NET 11.0.0-rc.1.26425.128 88 24 11.0.0-rc.1.26425.128 .NET 11.0.0-rc.1.26425.128 112 0
The narrow workaround that was exercised
This is the state change used by the passing absent-to-default control. The parent is already loaded and tracked. DetectChanges discovers the newly assigned owned object before its entry is marked Modified. The code is an excerpt from the companion, not a general instruction to mark every owned object modified.
Keep the persistence assertions unchanged when trying this option. In particular, assert the raw provider values after conversion and the owned object’s presence in a new context. Review any additional production behavior separately; this sample has no application concurrency tokens or production transaction workflow.
child.SetShared(SharedType.Create(EnumValue.Unknown, false));
context.ChangeTracker.DetectChanges();
context.Entry(child).Reference("Shared").TargetEntry!.State =
EntityState.Modified;
await context.SaveChangesAsync();
// Dispose the write context, then assert both raw stored values
// and the intended owned object from a new DbContext.Run the companion with its limits intact
The companion was compiled and run on Debian 13 x64 with .NET SDK 10.0.401 and 11.0.100-rc.1.26425.128, using SQLite relational databases and fabricated records. No SQL Server, production database, application workload or performance test was executed. The upstream report names SQL Server; these local results do not establish SQL Server behavior.
The first Sources link downloads the runnable sample, a release-review checklist, pinned package locks, per-case SQL and structured results. It runs only the 10.0.12 stable probe, its .NET 11 runtime control, and the EF 11 RC comparison. Install the stated SDKs separately from Microsoft if needed; the runner does not install an SDK or change production runtimes.
The historical 10.0.2 run is retained as evidence but its executable project and dependency graph are omitted from the reader download. That old graph includes SQLitePCLRaw.lib.e_sqlite3 2.1.11, covered by high-severity advisory GHSA-2m69-gcr7-jv3q. Do not copy the historical graph into an application.
Each EF 10.0.12 persistence process is expected to exit 1 because 24 assertions fail. The EF 11 RC process should exit 0. The matrix validator returns 0 only when all 336 current-sample cases match that documented pattern and the reports match the current run and source hash. That validator success does not make the failing persistence checks pass.
# Bash, from the extracted companion directory. # DOTNET10 and DOTNET11 identify separately installed SDK roots. DOTNET10=/path/to/dotnet10/dotnet \ DOTNET11=/path/to/dotnet11-preview/dotnet \ bash run-matrix.sh # Inspect results/<variant>/results.json and the per-case SQL logs. # The README also gives commands for the stable probe alone.
Separate release safety from data repair
A passing remedy establishes that the tested future write survives. It cannot reconstruct what an old NULL was intended to mean. NULL may be legitimate absence, a lost all-default write or part of a partial write.
If the affected path has already run, investigate it using available application evidence and the meaning of the record. Do not approve a blanket NULL-to-default backfill from this reproduction. Keep the repair decision separate from approval of the corrected write path.
Sources
- Download the persistence probe and release checklist (.zip)
- EF Core issue 37525 and the reported SQL Server model
- Corrected reporter example preserving shared column names
- Merged EF Core shared-column update fix 37751
- Maintainer decision to ship the fix in EF 11
- Official EF Core SQLite 10.0.12 package and version list
- Official EF Core SQLite 11 RC package and prerelease label
- GitHub reviewed SQLitePCLRaw advisory GHSA-2m69-gcr7-jv3q