ExecuteUpdate over projection uses derived-table copy of updated column, causing possible lost updates
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/100
Direzione di ricerca
Inizia riproducendo la query LINQ ExecuteUpdateAsync fornita e ispezionando l’SQL PostgreSQL generato. Segui il percorso di traduzione di ExecuteUpdate per determinare perché la tabella derivata fornisce la colonna aggiornata; il lavoro è completato quando l’espressione SET fa riferimento all’alias della destinazione dell’aggiornamento e gli incrementi concorrenti preservano il totale combinato.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Bug description
ExecuteUpdate over a projection can generate SQL where an increment expression uses a value read into a derived table instead of referencing the actual update target alias.
This can cause lost updates under concurrent executions.
The LINQ expression represents an atomic increment:
target.TotalQuantity = target.TotalQuantity + delta
But the generated SQL uses:
SET "TotalQuantity" = s2."TotalQuantity" + s2."QuantityDelta"
where s2."TotalQuantity" is read inside a derived table before the update.
For concurrent counter updates, the generated SQL should reference the update target alias:
SET "TotalQuantity" = s1."TotalQuantity" + s2."QuantityDelta"
because s1 is the actual row being updated.
Example lost-update scenario:
Initial TotalQuantity = 100
Transaction 1 delta = 10
Transaction 2 delta = 20
Both transactions can read s2.TotalQuantity = 100.
Transaction 1 updates row to 110.
Transaction 2 waits for the same row, then updates row to 100 + 20 = 120.
Expected final value: 130.
Possible final value: 120.
Your code
await context.ShortageOrderLines
.Where(line => line.ShortageOrderId == orderId)
.GroupBy(line => line.ShortageArticleTask!)
.Select(g => new
{
Task = g.Key,
QuantityDelta = g.Sum(l => l.Quantity)
})
.ExecuteUpdateAsync(setters => setters.SetProperty(
t => t.Task.TotalQuantity,
t => t.Task.TotalQuantity + t.QuantityDelta));
Generated SQL:
UPDATE shortage."ShortageArticleTasks" AS s1
SET "TotalQuantity" = s2."TotalQuantity" + s2."QuantityDelta"
FROM (
SELECT s0."Id", s0."TotalQuantity", COALESCE(sum(s."Quantity"), 0.0) AS "QuantityDelta"
FROM shortage."ShortageOrderLines" AS s
LEFT JOIN shortage."ShortageArticleTasks" AS s0 ON s."ShortageArticleTaskId" = s0."Id"
WHERE s."ShortageOrderId" = @orderId
GROUP BY s0."Id", s0."ArticleId", s0."LogisticVariantId", s0."TotalQuantity"
) AS s2
WHERE s1."Id" = s2."Id"
Expected SQL shape:
UPDATE shortage."ShortageArticleTasks" AS s1
SET "TotalQuantity" = s1."TotalQuantity" + s2."QuantityDelta"
FROM (
SELECT
s."ShortageArticleTaskId",
COALESCE(sum(s."Quantity"), 0.0) AS "QuantityDelta"
FROM shortage."ShortageOrderLines" AS s
WHERE s."ShortageOrderId" = @orderId
GROUP BY s."ShortageArticleTaskId"
) AS s2
WHERE s1."Id" = s2."ShortageArticleTaskId"
Stack traces
No exception is thrown. The generated SQL executes successfully, but it may have incorrect concurrency semantics for counter increments.
EF Core version
10.0.8
Database provider
Npgsql.EntityFrameworkCore.PostgreSQL
Target framework
net10.0
Operating system
Amazon Linux
IDE
VS 2026
- Lingua principale
- C#
- Stelle
- 14.8k
- Fork
- 3.4k
- Merge medio
- 3g 18h
- PR unite (30g)
- 159
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di dotnet/efcore
-
customer-reported
Difficoltà 4/5 3-5 giorni Idoneità per principianti 54/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 2 giorni
-
Regression in 11.0: replacing an owned instance or re-adding an entity with the same key sets unchanged OnAdd properties to nullForse già presa @seragAhmed l’ha presa oggi. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
dotnet/efcore#39617 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
customer-reported
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di dotnet/efcore
Issue simili
-
bug emitter:client:csharp
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
microsoft/typespec#12572 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
needs-area-label
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 2 giorni
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
area-networking
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70545 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 87/100
I maintainer di solito rispondono entro 1 giorno