dotnet / dotnet/roslyn

Entity Framework async method with await using returns false after true

Open
#84,688 2 comments 0 reactions 0 assignees View on GitHub
Area-Interactive Interactive-Debugging
Dominant language
C#
Stars
20.7k
Forks
4.3k
PR merge metrics
PR metrics pending

Description

_This issue has been moved from [a ticket on Developer Community](https://developercommunity.visualstudio.com/t/Entity-Framework-async-method-with-await/11122037)._

---
[severity:It's more difficult to complete my work]
I have an issue with async method hitting return statement twice.

Given this method:

```
public async Task<bool> DiscardDraftDeliveryNote(DiscardDraftDeliveryNoteCommand command, CancellationToken cancellationToken = default)
{
await using var dbContext = await DbFactory.CreateDbContextAsync(cancellationToken);
if (!command. DiscardAll)
{
var deliveryNote = await dbContext.DeliveryNotes.Where(f => f.DocumentStatus == DocumentStatus.Draft && f.Id == command. Id). FirstOrDefaultAsync(cancellationToken: cancellationToken);
if (deliveryNote != null)
{
dbContext.DeliveryNotes.Remove(deliveryNote);
var deletedCount = await dbContext.SaveChangesAsync(cancellationToken);
return deletedCount > 0;
}
else
{
Console.Write("Test");
return false;
}
}
else
{
var deliveryNotes = await dbContext.DeliveryNotes.Where(f => f.DocumentStatus == DocumentStatus.Draft). ToListAsync(cancellationToken: cancellationToken);
if (deliveryNotes != null && deliveryNotes.Any())
{
dbContext.DeliveryNotes.RemoveRange(deliveryNotes);
var deletedCount = await dbContext.SaveChangesAsync(cancellationToken);
return deletedCount > 0;
}
else
{
Console.Write("Test");
return false;
}
}
}
```

The method is triggered only once.
The execution of this method first turns to **!command. DiscardAll** branch, then it hits **return deletedCount >0**. Here we get equivalent to return true.

After hitting that return, execution in debugger jumps straight to the last **return false**, skipping everything in between.

The actual value that I receive in result of invocation is false.

If I change **await using** to just **using**, the method execution runs as it should.

The method is using EntityFramework to communicate with database.

I tried to extract CIL code for this method (if it helps):

```
.class public auto ansi beforefieldinit mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService
extends [System.Runtime]System.Object
implements [mojPOS.Core.Application.Client]mojPOS.Core.Application.Client.Features.DeliveryNote.Services.IDeliveryNoteService
{
.method public hidebysig newslot virtual final
instance class [System.Runtime]System.Threading.Tasks.Task`1<bool>
DiscardDraftDeliveryNote(class [mojPOS.Core.Application.Client]mojPOS.Core.Application.Client.Features.DeliveryNote.Commands.DiscardDraftDeliveryNoteCommand command,
[opt] valuetype [System.Runtime]System.Threading.CancellationToken cancellationToken) cil managed
{
.param [2] = nullref
// Code size 70 (0x46)
.maxstack 2
.locals init (class mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32' V_0)
IL_0000: newobj instance void mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::Create()
IL_000c: stfld valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::'<>t__builder'
IL_0011: ldloc.0
IL_0012: ldarg.0
IL_0013: stfld class mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::'<>4__this'
IL_0018: ldloc.0
IL_0019: ldarg.1
IL_001a: stfld class [mojPOS.Core.Application.Client]mojPOS.Core.Application.Client.Features.DeliveryNote.Commands.DiscardDraftDeliveryNoteCommand mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::command
IL_001f: ldloc.0
IL_0020: ldarg.2
IL_0021: stfld valuetype [System.Runtime]System.Threading.CancellationToken mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::cancellationToken
IL_0026: ldloc.0
IL_0027: ldc.i4.m1
IL_0028: stfld int32 mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::'<>1__state'
IL_002d: ldloc.0
IL_002e: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::'<>t__builder'
IL_0033: ldloca.s V_0
IL_0035: call instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::Start<class mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'>(!! 0&)
IL_003a: ldloc.0
IL_003b: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> mojPOS.Infrastructure.Client.Services.DeliveryNote.DeliveryNoteService/'<DiscardDraftDeliveryNote>d__32'::'<>t__builder'
IL_0040: call instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::get_Task()
IL_0045: ret
}

}
```

---
### Original Comments

#### Feedback Bot on 7/16/2026, 04:14 AM:

Based on similar issues resolved with Microsoft in the last 6 months, the following can help resolve this issue faster.


If not already included, please provide:


Detected: OS: 10.0.26200.0, VS: 18.7.4 (Professional 2026 build 18.7.12002.237), Project type: C#


Please share:



  1. Whether this reproduces in a new/simple C# project with the same EF Core code.

  2. The exact values of command. DiscardAll, command. Id, and whether deletedCount is definitely > 0 when the first return is reached.

  3. Whether you can reproduce with a minimal repro that excludes any other code around DbFactory, DbContext, or entity changes.

  4. The EF Core version and whether await using is required only for the DbContext or also for another disposable resource in the real code.

#### Miha Bogataj on 7/16/2026, 04:16 AM:

OS Build: 26200.8655

VS 2026: 18.7.4


I also prepared a sample project where you can reproduce the issue. The issue is reproducable in the method RunWithAwaitUsing.

AsyncDisposalReturnSample.zip

#### Feedback Bot on 7/16/2026, 10:55 PM:

We have directed your feedback to the appropriate engineering team for further evaluation. The team will review the feedback and notify you about the next steps.

#### Feedback Bot on 7/29/2026, 10:09 AM:

This issue is currently being investigated. Our team will get back to you if either more information is needed, a workaround is available, or the issue is resolved.

---
### Original Solutions

#### Chuck Ries [MSFT] solved on 7/29/2026, 10:11 AM, undefined votes:

Miha Bogataj Thank you for reporting this. I can reproduce the issue, this is purely a quirk of where the debugger is shown to be stopping, the multiple return statements are not actually being executed. Stepping from the first return statement, the stopping at the second return is actually stopped at the invisible call to await dbContext.DisposeAsync.


I will investigate a fix (this is likely a compiler issue), but the visual quirk you are seeing is not affecting the behavior of the program.

Contributor guide

Open the contributing guide

Research direction

Use the attached AsyncDisposalReturnSample.zip and its RunWithAwaitUsing method as the reproduction; first verify the debugger stops at the implicit DisposeAsync call after the return. Compare the observed stepping with the reported return locations, and consider the issue done when the debugger no longer presents async disposal as a second return or the behavior is otherwise documented as expected.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
compilers, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.