diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..2ffe6ba --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Bash(wc -l /d/PersonalGitRepos/EFCore.Sqlite.Concurrency/EntityFrameworkCore.Sqlite.Concurrency/src/*.cs)", + "Bash(dotnet build:*)", + "WebFetch(domain:docs.binance.us)", + "Bash(xargs grep *)", + "Bash(dotnet:*)", + "Bash(dotnet build --help:*)", + "Bash(python -c \"import json,sys; json.load\\(open\\(r'C:\\\\Users\\\\mikeg\\\\.claude\\\\settings.json'\\)\\); print\\('valid'\\)\")" + ] + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 24da656..e39d818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [10.1.1] - 2026-10-05 + +### Security +- **Upgraded SQLitePCLRaw.core to 2.1.12** — addresses vulnerability [GHSA-2m69-gcr7-jv3q](https://github.com/advisories/GHSA-2m69-gcr7-jv3q) + (SQLite embedded library security fix). All transitive `SQLitePCLRaw.*` packages updated to 2.1.12. + Vulnerability suppression comment removed from project file. + +### Compatibility +- **Zero API changes.** Fully backward compatible with v10.1.0. +- **Zero migration effort.** All existing code works without modification. + +--- + ## [10.1.0] - 2026-07-05 ### Added @@ -95,7 +108,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `SqliteDiagnostics` — opt-in Spectre.Console diagnostics UI (`-p:IncludeSpectre=true`). - `MemoryPackExtensions` — opt-in MemoryPack serialization (`-p:IncludeMemoryPack=true`). -[Unreleased]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.1.0...HEAD +[Unreleased]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.1.1...HEAD +[10.1.1]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.1.0...v10.1.1 [10.1.0]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.0.3...v10.1.0 [10.0.3]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.0.2...v10.0.3 [10.0.2]: https://github.com/CornerstoneCode/EntityFrameworkCore.Sqlite.Concurrency/compare/v10.0.1...v10.0.2 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0ca4ffb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Build Commands + +```bash +# Restore (locked packages enforced via packages.lock.json) +dotnet restore EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj + +# Build +dotnet build EntityFrameworkCore.Sqlite.Concurrency.sln -c Release + +# Pack NuGet package +dotnet pack EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj -c Release --no-build -o out + +# Build with optional dependencies +dotnet build ... -p:IncludeMemoryPack=true # enables MemoryPackExtensions.cs +dotnet build ... -p:IncludeSpectre=true # enables SqliteDiagnostics.cs (Spectre.Console) +``` + +**No test projects exist.** CI only restores, builds, and packs. + +## Project Overview + +This is a .NET 10 NuGet library (`EntityFrameworkCore.Sqlite.Concurrency`) that adds write serialization, WAL-mode optimization, retry logic, and bulk-insert performance on top of `Microsoft.EntityFrameworkCore.Sqlite`. It is a pure library — no apps, no test harness. + +Target: `net10.0` | Root namespace: `EntityFrameworkCore.Sqlite.Concurrency` + +## Architecture + +All source lives in `EntityFrameworkCore.Sqlite.Concurrency/src/`. + +### Component Map + +| File | Role | +|---|---| +| `SqliteConcurrencyOptions.cs` | Central configuration POCO; `Validate()` called at startup | +| `SqliteConnectionEnhancer.cs` | Process-global registry; owns all shared static state | +| `SqliteConcurrencyInterceptor.cs` | EF Core interceptor wiring; applies PRAGMAs and rewrites `BEGIN` | +| `SqliteConcurrencyExtensions.cs` | Public API — `UseSqliteWithConcurrency()`, `SaveChangesSerializedAsync()`, `ExecuteWithRetryAsync()`, `BulkInsertOptimizedAsync()` | +| `SqliteConcurrencyServiceCollectionExtensions.cs` | DI registration (`AddConcurrentSqliteDbContext()`, `AddConcurrentSqliteDbContextFactory()`) | +| `ThreadSafeFactory.cs` | DI-free factory for environments without `IServiceCollection` | +| `ThreadSafeSqliteContext` | Optional generic base `DbContext` with write/read/bulk APIs built in | +| `SqliteErrorCodes.cs` | Internal constants and classifiers for SQLite extended error codes | +| `MemoryPackExtensions.cs` | `#if INCLUDEMEMORYPACK` — opt-in MemoryPack integration | +| `SqliteDiagnostics.cs` | `#if INCLUDESPECTRE` — opt-in Spectre.Console diagnostics UI | + +### Key Design Decisions + +**Write serialization has two layers:** +1. Application-level: one `SemaphoreSlim(1,1)` per database file stored in `SqliteConnectionEnhancer._writeLocks`, keyed by normalized connection string. +2. SQLite-level: `busy_timeout` PRAGMA applied on every connection open. + +**`SqliteConnectionEnhancer` is the process-global brain:** +- `_writeLocks` — write-lock registry (one semaphore per DB file) +- `_interceptors` — enforces consistent options; throws `ArgumentException` on option mismatch for same DB +- `_initializedDatabases` / `_preparedDatabases` — once-per-process guards (database-scoped PRAGMAs applied once; connection-scoped PRAGMAs applied on every `ConnectionOpened`) +- `_connectionStringCache` — memoizes optimized connection strings; rejects `Cache=Shared` (incompatible with WAL) +- `IsWriteLockHeld` (`AsyncLocal`) — reentrancy guard preventing deadlock on nested write calls + +**`SQLITE_BUSY_SNAPSHOT` requires full operation restart** (not just statement retry) because the read snapshot is stale. `ExecuteWithRetryAsync()` reruns the entire user-supplied lambda. + +**Pre-open file preparation** (`PrepareForConnectionOpen`) strips `FileAttributes.ReadOnly` and deletes stale `.db-shm` — handles a Windows MSBuild quirk where `CopyToOutputDirectory` marks the DB read-only. + +**Options equality excludes `LoggerFactory`** — used to enforce consistent interceptor registration per database; logger infrastructure is not behavioral identity. + +**Conditional compilation** keeps the default package lean: +- `#if INCLUDEMEMORYPACK` — requires `-p:IncludeMemoryPack=true` +- `#if INCLUDESPECTRE` — requires `-p:IncludeSpectre=true` + +### Retry Backoff + +Exponential backoff with full jitter: delay ∈ `[baseDelay, 2×baseDelay]` using `Random.Shared`. Prevents thundering-herd storms on concurrent retries. + +## Project File Notes + +- `true` — always update `packages.lock.json` after adding/changing dependencies +- `enable` and `true` — missing XML doc comments produce build warnings +- `` activates deterministic builds on GitHub Actions +- SourceLink and `EmbedUntrackedSources` are configured for NuGet symbol packages diff --git a/EFCore.Sqlite.Concurrency.Test/EFCore.Sqlite.Concurrency.Test.csproj b/EFCore.Sqlite.Concurrency.Test/EFCore.Sqlite.Concurrency.Test.csproj index c282f5d..28731b5 100644 --- a/EFCore.Sqlite.Concurrency.Test/EFCore.Sqlite.Concurrency.Test.csproj +++ b/EFCore.Sqlite.Concurrency.Test/EFCore.Sqlite.Concurrency.Test.csproj @@ -8,13 +8,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj b/EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj index c6ecbc3..f6ae743 100644 --- a/EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj +++ b/EntityFrameworkCore.Sqlite.Concurrency/EFCore.Sqlite.Concurrency.csproj @@ -12,7 +12,7 @@ EntityFrameworkCore.Sqlite.Concurrency - 10.1.0 + 10.1.1 Mike Gotfryd @@ -20,27 +20,45 @@ © 2026 Cornerstone Code. All rights reserved. - + - Fixes SQLITE_BUSY / "database is locked" / SQLITE_BUSY_SNAPSHOT errors in Entity - Framework Core + SQLite apps. Provides Channel-based write serialization, automatic - BEGIN IMMEDIATE transaction upgrades, exponential-backoff retry, WAL-mode optimization, - and 10x faster bulk inserts. Drop-in replacement for UseSqlite() targeting .NET 10 - and netstandard 2.0. + Solve SQLite concurrency errors (SQLITE_BUSY, "database is locked", SQLITE_BUSY_SNAPSHOT) in + Entity Framework Core with drop-in write serialization. Implements Channel-based queuing, + automatic BEGIN IMMEDIATE transaction upgrades, exponential-backoff retry with full jitter, + WAL-mode optimization, and 10x faster bulk inserts. Solves multi-writer, multi-reader conflicts + without connection pooling complexity. Works with .NET 10 and netstandard 2.0 runtimes. + Security: Updated SQLitePCLRaw.core to 2.1.12 (fixes GHSA-2m69-gcr7-jv3q). - - sqlite sqlite3 entity-framework-core efcore concurrency thread-safe multi-threading database-locked sqlite-busy sqlite-busy-snapshot error-5 write-serialization channel-queue netstandard2 efcore-concurrency performance bulk-insert parallel-reads write-ahead-logging wal dotnet-10 entity-framework orm database-provider high-performance async transactions locking queue background-service task-whenall + + sqlite sqlite3 entity-framework-core efcore concurrency thread-safe multi-threading database-locked sqlite-busy sqlite-busy-snapshot error-5 write-serialization channel-queue netstandard2 efcore-concurrency performance bulk-insert parallel-reads write-ahead-logging wal dotnet-10 entity-framework orm database-provider high-performance async transactions locking queue background-service task-whenall sql-locking deadlock-prevention database-concurrency write-queue security vulnerability patch - + + single - background writer. SemaphoreSlim wakes all N waiters simultaneously (thundering herd); - Channel parks callers in FIFO order — no wakeup storm, fairer scheduling, lower CPU. + background writer. Eliminates thundering herd; FIFO fairness; lower CPU under concurrent load. Zero API change — existing call sites benefit automatically. • WriteQueueCapacity option: new int? on SqliteConcurrencyOptions (null = unbounded). Set a value to create a bounded channel with back-pressure (BoundedChannelFullMode.Wait). @@ -88,21 +106,20 @@ NO BREAKING CHANGES — all existing call sites compile and behave correctly wit - - - + + - - - - + + + + - - - - + + + + @@ -117,15 +134,9 @@ NO BREAKING CHANGES — all existing call sites compile and behave correctly wit - + - - - - diff --git a/EntityFrameworkCore.Sqlite.Concurrency/packages.lock.json b/EntityFrameworkCore.Sqlite.Concurrency/packages.lock.json index b1bf311..f1cc546 100644 --- a/EntityFrameworkCore.Sqlite.Concurrency/packages.lock.json +++ b/EntityFrameworkCore.Sqlite.Concurrency/packages.lock.json @@ -4,44 +4,46 @@ ".NETStandard,Version=v2.0": { "Microsoft.Data.Sqlite": { "type": "Direct", - "requested": "[7.0.20, )", - "resolved": "7.0.20", - "contentHash": "IIjX4K1EjTg73Ed/ZsjHWyAtcRAGoS42eiHa30HIbTYO62dGgq+yWRpkxZbTSvEjUlUvUmWORxrEmKloRianUw==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "xUq9xUc653ue7ac2rMfCaf8UQN+U3zS8poWYlugycpvJllplMM0Mk7v/+MM0CTlu9zGvbGML4BPH8YWhrUOfGw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.20", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "10.0.12", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.12", + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Direct", - "requested": "[8.0.2, )", - "resolved": "8.0.2", - "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "9/qymSh7hVDMGTGwrLz8MRp5zRyXy9adGDOs4HwRdnLil3oZGYuWeZjbmHgCQ9BL1qBroVfgUK3U/nb61617Cw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.Bcl.AsyncInterfaces": "10.0.12", + "System.Threading.Tasks.Extensions": "4.6.3" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[8.0.3, )", - "resolved": "8.0.3", - "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "+24lC4plfbEDNfLAdTV/SWKS7dW+16X4HdydO3R++134kSNTzcbYA4KpR1Hdh6uWisB8Za3AzwyOn+K+NxWIug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.12", + "System.Buffers": "4.6.1", + "System.Diagnostics.DiagnosticSource": "10.0.12", + "System.Memory": "4.6.3" } }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.300, )", - "resolved": "10.0.300", - "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", + "requested": "[10.0.401, )", + "resolved": "10.0.401", + "contentHash": "LGmlwgP1Cx37JEWzyjS0o1/+xs/s/e3E2TBSuogI5ePA/9L0pfIfeYX0k5in7Bfcw8Nn2y2sG9jXxydTjiR2Fg==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.300", - "Microsoft.SourceLink.Common": "10.0.300", - "System.IO.Hashing": "10.0.8" + "Microsoft.Build.Tasks.Git": "10.0.401", + "Microsoft.SourceLink.Common": "10.0.401", + "System.IO.Hashing": "10.0.12" } }, "NETStandard.Library": { @@ -55,35 +57,36 @@ }, "System.Threading.Channels": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "/iUnzLpSBUUnnIHj/jSjhyEqhgm2eo/D2SF1Ga9ojuMqsua+QwKqByukl0UA9/Rth+P0sVJhXZojzgpV0A5Kdw==", "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.Bcl.AsyncInterfaces": "10.0.12", + "System.Threading.Tasks.Extensions": "4.6.3" } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "resolved": "10.0.12", + "contentHash": "vHPomsJPqUvf1ExdzCCSEUMQqloONPTBMGqNqBUdr4UgSTs9bCzH1oLkuS6WgUgIu+xqWngQbjelUBNlDuzLWQ==", "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Threading.Tasks.Extensions": "4.6.3" } }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.300", - "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "resolved": "10.0.401", + "contentHash": "ZYctNuT10V9IYyCFydy63DXx0ggZQuynuzQOdLvW62dPgzjIz7f0ISEP75RGiq1jFQh8p6TmGSqxeQZQ87LCig==", "dependencies": { - "System.IO.Hashing": "10.0.8" + "System.IO.Hashing": "10.0.12" } }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.20", - "contentHash": "B8/42FqMMRq3mf5qnJOF7XhB/UEcHcwdmuI6D025uTRcPM0R1DbMw+6C77Op1f/qKAHsDHmdTID5CZLpYBvmlw==", + "resolved": "10.0.12", + "contentHash": "2aL9eL5HQr0V4b8V909SD+jQAx6qnTTl7sT/v3D8dIvmr2UGdpZIQxqm1pj2Js3e4EYTvpw0oMH7DjapJJuPPw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.NETCore.Platforms": { @@ -93,37 +96,37 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.300", - "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + "resolved": "10.0.401", + "contentHash": "u3rLxIwi/9MqDFaWGE/QQgLR1NBEzLOW2lv5+9OrZPDBYIAmFdYSWCWrR1ufpXWOqFn+x02TgKropl/oDuHmgA==" }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.12", + "contentHash": "mAgscpQMLw5/nfA1Q5oJVAT29yROUo1ifZGbbTpx/lwZpSxMUGoYbKfmvdm8oXER+RzxqBmmQzeBEVKfeHv2nw==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.12", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.12" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.12", + "contentHash": "ETpNw9DY3ckWLgRRAeCHj+GKOuPi61aeczkXhgHexUvqoZBAYg8RYESE2J7O1M7+o6QbdSEZwrw9bfqztUVWXg==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.12", + "contentHash": "fWi8Dbknuhgg72fWinIdjXVaqO1hHL4YBBwVLnr7e1c9TAZwJ0QE38j9syW1hwx6HaqEVTwI+O07WPdZn8Rp0w==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.12", + "contentHash": "W3oH4XIfCzFrgUSDKHhN6N+dgzA5YHOR2VxX8GB6Qy7CyrJJgxPEG8NirgYWlPQC5P2jz2knSsexWu4tDUL33g==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.12" } }, "System.Buffers": { @@ -131,10 +134,19 @@ "resolved": "4.6.1", "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "10.0.12", + "contentHash": "CRZTWYO0rzEcJN/6SuN1kMvQOJT7vyHZ64CCkQs0RRWTj7kH2KMkaMalbsbSI/eg5dS5JAQfQFFuuWgn+XRMsQ==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "10.0.8", - "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==", + "resolved": "10.0.12", + "contentHash": "jDix4bBMYnpZdSPcnY+KDV6ik3SRMzpMKby/bZl/XUwIiflwRNAFZ0oOl61R/pSaveIJ8t1gs2BUlrGsPs/bcg==", "dependencies": { "System.Buffers": "4.6.1", "System.Memory": "4.6.3" @@ -162,229 +174,229 @@ }, "System.Threading.Tasks.Extensions": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "resolved": "4.6.3", + "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } } }, "net10.0": { "Microsoft.Data.Sqlite": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "/eBwiZPcNisn0qZX+Zk4YCftlK/vnoWqv7hHnmSk8MjPxFdYYkmPObpogT0MfCCWN6oAIZnMCo0SoOtZlbbmgQ==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "xUq9xUc653ue7ac2rMfCaf8UQN+U3zS8poWYlugycpvJllplMM0Mk7v/+MM0CTlu9zGvbGML4BPH8YWhrUOfGw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "10.0.9", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", - "SQLitePCLRaw.core": "2.1.11" + "Microsoft.Data.Sqlite.Core": "10.0.12", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.12", + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "HH9/nnRF/YmRrc3hUlgXjMBYKH5kFmd5UWC81l9U0ySQhwHTcgvDPSewB8DyQHzFJzNGgG7VFK6ynC6+XQz9WA==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "rRXkKBWHjtngj2DYqSXOThGpqiR0LQkxk0b0PIVDhVEzfwpgOR7dbKMw1/My8unDCTCwn/blclR7imxntYGvEg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.9", - "Microsoft.Extensions.Caching.Memory": "10.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.9", - "Microsoft.Extensions.DependencyModel": "10.0.9", - "Microsoft.Extensions.Logging": "10.0.9", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", - "SQLitePCLRaw.core": "2.1.11" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.12", + "Microsoft.Extensions.Caching.Memory": "10.0.12", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.12", + "Microsoft.Extensions.DependencyModel": "10.0.12", + "Microsoft.Extensions.Logging": "10.0.12", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.12", + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "NijozhERJDIaJ4k5TSMy1jOi0cSC2HfkvRD/Sl+kGSSKgVbFnF4GxgtMN/MrzHB8D1JxIrD4xSer9Blh9v3axQ==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "lXyK2O5GoYvfxW8eCFcD16JFbcoSTM1sJkAM0UHS1jZyl9NYMW64Tqm6OQFT0IDBjZi+xHt95/Zg+nxZhGFhZg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.12" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "9S/DFt4cohlMPpzIxjG6kk0L8MuN2vDm9pbMCulxtJzzk82oJHVLBd8vuQxaPskaYQwKqmFmbannf5eoChgjYg==", + "requested": "[10.0.12, )", + "resolved": "10.0.12", + "contentHash": "+24lC4plfbEDNfLAdTV/SWKS7dW+16X4HdydO3R++134kSNTzcbYA4KpR1Hdh6uWisB8Za3AzwyOn+K+NxWIug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.12" } }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.300, )", - "resolved": "10.0.300", - "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", + "requested": "[10.0.401, )", + "resolved": "10.0.401", + "contentHash": "LGmlwgP1Cx37JEWzyjS0o1/+xs/s/e3E2TBSuogI5ePA/9L0pfIfeYX0k5in7Bfcw8Nn2y2sG9jXxydTjiR2Fg==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.300", - "Microsoft.SourceLink.Common": "10.0.300", - "System.IO.Hashing": "10.0.8" + "Microsoft.Build.Tasks.Git": "10.0.401", + "Microsoft.SourceLink.Common": "10.0.401", + "System.IO.Hashing": "10.0.12" } }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.300", - "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "resolved": "10.0.401", + "contentHash": "ZYctNuT10V9IYyCFydy63DXx0ggZQuynuzQOdLvW62dPgzjIz7f0ISEP75RGiq1jFQh8p6TmGSqxeQZQ87LCig==", "dependencies": { - "System.IO.Hashing": "10.0.8" + "System.IO.Hashing": "10.0.12" } }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "iZrONyMKPjxfVZnUktqO30QjzNwAGH+AxM61s8lKQnVhgbQ3bn0hiXI129ZmVicEbIcwljyy2OVsIYUR51ZHKQ==", + "resolved": "10.0.12", + "contentHash": "2aL9eL5HQr0V4b8V909SD+jQAx6qnTTl7sT/v3D8dIvmr2UGdpZIQxqm1pj2Js3e4EYTvpw0oMH7DjapJJuPPw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.11" + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "tu85SRzOT021V7EQlViCiAE7TqldVn469Y6lt5TEn/+XC4/MeNCHgMRSxqYuWqvF4zAQZUhCmtNEZuM3ss4LeA==", + "resolved": "10.0.12", + "contentHash": "e7OrVN8U5yr4kuwdTHWBHGVQIVsGfMkAr0Ej1/BnrS989Q0XBVANpIzNZ2bc1GvMZ0/XwMt5InTnmKskxGy99Q==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.9", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.9", - "Microsoft.Extensions.Caching.Memory": "10.0.9", - "Microsoft.Extensions.Logging": "10.0.9" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.12", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.12", + "Microsoft.Extensions.Caching.Memory": "10.0.12", + "Microsoft.Extensions.Logging": "10.0.12" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "GRMaiPkqYna/gCsyDffYDWmefGPC3hDrdMw+2rrGcQwhs6uZOsaMQXMJnoXQ35tx9SkBV2ieRRU9N/jLOO6BZw==" + "resolved": "10.0.12", + "contentHash": "kDrux7T6C3V/YYYe2LXr7x6AEsVyq5Y2YoTE9er1SwbDtap3z1g8RgxNh5eKQVyB4T+DKvBBJ4Ezd/VZzJQJZg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "aiEFB+C5EsZGqxvMPazE07hbWsp4iPaufJpanGt5O+lrwv7mJLrqma5haVIgFAPCyhQkmk75XSCEubT1zUjxtA==" + "resolved": "10.0.12", + "contentHash": "Kd4o2oO1A6dfKxjz5LS16QyLtpCFTUiFWwmBABETAXRsSImBdRdWeKjSgbbRhAMiV3a3/nWr95ZtzClT4uaADQ==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "dEoyYKgiaZHHgOFm1WMWm1sFEsEuhPWufX4L9PekKtqd/RaIcPjkCjvbrVvJtApErb5wPSJhYvnTlxhH+p9h2g==", + "resolved": "10.0.12", + "contentHash": "OiHmr8XzX96dbgMsYGe8qoqg4KxibdZG2r0QUVJBGktFInOp8IrABe8jvcbo5hrqeWfbwpK7fCEBiEPbY/ik4A==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.9", - "Microsoft.Extensions.Caching.Memory": "10.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.9", - "Microsoft.Extensions.Logging": "10.0.9" + "Microsoft.EntityFrameworkCore": "10.0.12", + "Microsoft.Extensions.Caching.Memory": "10.0.12", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.12", + "Microsoft.Extensions.Logging": "10.0.12" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "3bPEmACAaPJJSw+m5XwTSi3yZnVtaifa4d8gLsNMzW0Qu28jS5kADSfgJRBlq49RJ1K098VCzEDRJwM8gE6f2w==", + "resolved": "10.0.12", + "contentHash": "DAGu68RfIDD5d6qYhtQqMUsRS7hKKFqHI6h/9VUa4FjMcQ8+IJA0rK+1pDZtrDJ+yi3YAG0L2TSViZ0csEDM0w==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "10.0.9", - "Microsoft.EntityFrameworkCore.Relational": "10.0.9", - "Microsoft.Extensions.Caching.Memory": "10.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.9", - "Microsoft.Extensions.DependencyModel": "10.0.9", - "Microsoft.Extensions.Logging": "10.0.9", - "SQLitePCLRaw.core": "2.1.11" + "Microsoft.Data.Sqlite.Core": "10.0.12", + "Microsoft.EntityFrameworkCore.Relational": "10.0.12", + "Microsoft.Extensions.Caching.Memory": "10.0.12", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.12", + "Microsoft.Extensions.DependencyModel": "10.0.12", + "Microsoft.Extensions.Logging": "10.0.12", + "SQLitePCLRaw.core": "2.1.12" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "5fGxcw2vuYp8s0wio9H1ECiuk4iKSdTIlNuigdLIrkhg+5XAwgFVDB/5Ots3pfN/QhABLYXutA79JFtnUKDSHA==", + "resolved": "10.0.12", + "contentHash": "sI3A6MUAyZwccLnwq0cXuRmC3WBtXieimBWafXSfhRJ4jnM2uGVXrqImDDVyW27u4vVaTOGlJxNJd8d1FGvCNQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.Primitives": "10.0.12" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "G9mregdatGWMCQWeCw012LDeJVP7G/XIxH8Ddbjc8bD1//dA+8VVQdcRE9jI1moyoJxSSZhHITUnNQ8FUDl5+Q==", + "resolved": "10.0.12", + "contentHash": "fhC3DHcBcFF4bXvHIxFtXJYNut7q7ZItqNFcgWfrIgmpVImlcEKNHhE7ztpNSPJYMHYYStS5QGND2I5mC26yeA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9", - "Microsoft.Extensions.Logging.Abstractions": "10.0.9", - "Microsoft.Extensions.Options": "10.0.9", - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.Caching.Abstractions": "10.0.12", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.12", + "Microsoft.Extensions.Logging.Abstractions": "10.0.12", + "Microsoft.Extensions.Options": "10.0.12", + "Microsoft.Extensions.Primitives": "10.0.12" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "qGhRPd3VxfLV9UqatVOiD9mAeUbj2KiMwGFYC5uXlzExiZQoe4X/hdmzGIU7BQjNLTqCnnbTHVyBglG3668/HA==", + "resolved": "10.0.12", + "contentHash": "8xaGcvS/qZ1otoxPQCEJkNva389CVL/plNcvIETZhQTETYdRkYDPEYhUMoAGONo4FU45ufdfE0j29AfWVVj0wA==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.Primitives": "10.0.12" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "g41l/30G3K4B/d/L8kjux0+30e27c8D0FVQ/PFCpbekgfDpj9mnDhieP67EqXWvl1EWNeZh2rpR4F5B/jcDOHA==" + "resolved": "10.0.12", + "contentHash": "9/qymSh7hVDMGTGwrLz8MRp5zRyXy9adGDOs4HwRdnLil3oZGYuWeZjbmHgCQ9BL1qBroVfgUK3U/nb61617Cw==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "SCDTQ6HubnRvTUjR7dgMKHZvNoCb03t44ttHL8trlFTGgfDteWn/0nRdOxDhcI+lTWhKgd/flCVJEtAOPhSLNg==" + "resolved": "10.0.12", + "contentHash": "rDPQVTxh/zMTDF7wHlRqL/jjZbwjAP6315ydBxj51pZW7qkAwGwjoSiMutxYI91xmOE3ZXjAGHbYRqzyJV7Urw==" }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "N7Gm9SjugYjmmnhwbBKC9DFqGqjfJvh6YfOJgtwh0AW0Xpok3dIVors1ik050XmUxKAgAc7nNngDIJyFb06K2g==", + "resolved": "10.0.12", + "contentHash": "6I46fTPfgYkrjRYfRXbho9WOvOelTnNjWuZws/hzGHDASH1LEJeA4VKK9k3wJvido8o7jJSB5WkMTonX7HM1bA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.9", - "Microsoft.Extensions.Logging.Abstractions": "10.0.9", - "Microsoft.Extensions.Options": "10.0.9" + "Microsoft.Extensions.DependencyInjection": "10.0.12", + "Microsoft.Extensions.Logging.Abstractions": "10.0.12", + "Microsoft.Extensions.Options": "10.0.12" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "hyNdX4c2UwkRkzb9byw0H2DQkRzwBM3mzY2sCM9egwzTyg8dvQJmp5noQHGEaaCORQrNK3DD2gREBsc2DlXS4A==", + "resolved": "10.0.12", + "contentHash": "TDYD33TSRpXKZWlmTXNlj5kCihxatmv2Ec1u6C+bMYLphCS7PoSLE9Pjd/nunDoE7yETk+LLKjVJX78HYtWjpA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9", - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.12", + "Microsoft.Extensions.Primitives": "10.0.12" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "fmEbAUFsaIKirgLt/lYhuFRBwhcSJN31jjHgCdbQxJiWOum6EdLjkbgGuukSP9z/a+9LibaxII/kF+GwOXgC4g==" + "resolved": "10.0.12", + "contentHash": "dYfCLR52UA+3DL7C4I/pvSaRPkNqxrUAQmbFL2u0zvYKKzqgrFCJl08Df+F1aYc8leu9JvpC9bsURUdpExcBXQ==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.300", - "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + "resolved": "10.0.401", + "contentHash": "u3rLxIwi/9MqDFaWGE/QQgLR1NBEzLOW2lv5+9OrZPDBYIAmFdYSWCWrR1ufpXWOqFn+x02TgKropl/oDuHmgA==" }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.11", - "contentHash": "DC4nA7yWnf4UZdgJDF+9Mus4/cb0Y3Sfgi3gDnAoKNAIBwzkskNAbNbyu+u4atT0ruVlZNJfwZmwiEwE5oz9LQ==", + "resolved": "2.1.12", + "contentHash": "mAgscpQMLw5/nfA1Q5oJVAT29yROUo1ifZGbbTpx/lwZpSxMUGoYbKfmvdm8oXER+RzxqBmmQzeBEVKfeHv2nw==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.11", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.11" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.12", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.12" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.11", - "contentHash": "PK0GLFkfhZzLQeR3PJf71FmhtHox+U3vcY6ZtswoMjrefkB9k6ErNJEnwXqc5KgXDSjige2XXrezqS39gkpQKA==" + "resolved": "2.1.12", + "contentHash": "ETpNw9DY3ckWLgRRAeCHj+GKOuPi61aeczkXhgHexUvqoZBAYg8RYESE2J7O1M7+o6QbdSEZwrw9bfqztUVWXg==" }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.11", - "contentHash": "Ev2ytaXiOlWZ4b3R67GZBsemTINslLD1DCJr2xiacpn4tbapu0Q4dHEzSvZSMnVWeE5nlObU3VZN2p81q3XOYQ==" + "resolved": "2.1.12", + "contentHash": "fWi8Dbknuhgg72fWinIdjXVaqO1hHL4YBBwVLnr7e1c9TAZwJ0QE38j9syW1hwx6HaqEVTwI+O07WPdZn8Rp0w==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.11", - "contentHash": "Y/0ZkR+r0Cg3DQFuCl1RBnv/tmxpIZRU3HUvelPw6MVaKHwYYR8YNvgs0vuNuXCMvlyJ+Fh88U1D4tah1tt6qw==", + "resolved": "2.1.12", + "contentHash": "W3oH4XIfCzFrgUSDKHhN6N+dgzA5YHOR2VxX8GB6Qy7CyrJJgxPEG8NirgYWlPQC5P2jz2knSsexWu4tDUL33g==", "dependencies": { - "SQLitePCLRaw.core": "2.1.11" + "SQLitePCLRaw.core": "2.1.12" } }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "10.0.8", - "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" + "resolved": "10.0.12", + "contentHash": "jDix4bBMYnpZdSPcnY+KDV6ik3SRMzpMKby/bZl/XUwIiflwRNAFZ0oOl61R/pSaveIJ8t1gs2BUlrGsPs/bcg==" } } } diff --git a/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.nupkg b/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.nupkg new file mode 100644 index 0000000..bb61e16 Binary files /dev/null and b/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.nupkg differ diff --git a/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.snupkg b/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.snupkg new file mode 100644 index 0000000..9b78a3b Binary files /dev/null and b/out/EntityFrameworkCore.Sqlite.Concurrency.10.1.1.snupkg differ