diff --git a/async-csharp/IAsyncEnumerableWithYield/.gitignore b/async-csharp/IAsyncEnumerableWithYield/.gitignore
new file mode 100644
index 000000000..ec260fd05
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/.gitignore
@@ -0,0 +1,4 @@
+# SQLite database the API creates on first run
+*.db
+*.db-shm
+*.db-wal
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.sln b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.sln
new file mode 100644
index 000000000..2bc483d45
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.sln
@@ -0,0 +1,90 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskOfIEnumerable", "TaskOfIEnumerable\TaskOfIEnumerable.csproj", "{CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncYield", "SyncYield\SyncYield.csproj", "{29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IAsyncEnumerableWithYield", "IAsyncEnumerableWithYield\IAsyncEnumerableWithYield.csproj", "{296E143D-0CCD-48CA-9054-5E918F8B5462}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IAsyncEnumerableWithYieldApi", "IAsyncEnumerableWithYieldApi\IAsyncEnumerableWithYieldApi.csproj", "{01A66C19-2011-41A9-876A-E02A45431465}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{32DFB249-9EA5-4E7C-8BBA-E01853778BE7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|x64.Build.0 = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Debug|x86.Build.0 = Debug|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|x64.ActiveCfg = Release|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|x64.Build.0 = Release|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|x86.ActiveCfg = Release|Any CPU
+ {CEF7D2E3-0B02-4E40-9084-C29DCCA519CD}.Release|x86.Build.0 = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|x64.Build.0 = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Debug|x86.Build.0 = Debug|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|x64.ActiveCfg = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|x64.Build.0 = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|x86.ActiveCfg = Release|Any CPU
+ {29FC1E6D-1BB0-4AE1-879B-B97A2BC0AD44}.Release|x86.Build.0 = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|x64.Build.0 = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Debug|x86.Build.0 = Debug|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|Any CPU.Build.0 = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|x64.ActiveCfg = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|x64.Build.0 = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|x86.ActiveCfg = Release|Any CPU
+ {296E143D-0CCD-48CA-9054-5E918F8B5462}.Release|x86.Build.0 = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|x64.Build.0 = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Debug|x86.Build.0 = Debug|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|Any CPU.Build.0 = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|x64.ActiveCfg = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|x64.Build.0 = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|x86.ActiveCfg = Release|Any CPU
+ {01A66C19-2011-41A9-876A-E02A45431465}.Release|x86.Build.0 = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|x64.Build.0 = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Debug|x86.Build.0 = Debug|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|x64.ActiveCfg = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|x64.Build.0 = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|x86.ActiveCfg = Release|Any CPU
+ {32DFB249-9EA5-4E7C-8BBA-E01853778BE7}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.csproj b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.csproj
new file mode 100644
index 000000000..dfb40caaf
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/Program.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/Program.cs
new file mode 100644
index 000000000..cf738af6d
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYield/Program.cs
@@ -0,0 +1,17 @@
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Start");
+
+await foreach (var item in FetchItems())
+{
+ Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {item}");
+}
+
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: End");
+
+static async IAsyncEnumerable FetchItems()
+{
+ for (int i = 1; i <= 10; i++)
+ {
+ await Task.Delay(1000);
+ yield return i;
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ItemsController.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ItemsController.cs
new file mode 100644
index 000000000..1d1b8c08e
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ItemsController.cs
@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+[Route("api/[controller]")]
+[ApiController]
+public class ItemsController : ControllerBase
+{
+ private readonly ItemContext _context;
+
+ public ItemsController(ItemContext context)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ }
+
+ [HttpGet]
+ public IAsyncEnumerable- GetItems() =>
+ _context.Items.AsNoTracking().AsAsyncEnumerable();
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ValuesController.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ValuesController.cs
new file mode 100644
index 000000000..1228caf3e
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Controllers/ValuesController.cs
@@ -0,0 +1,21 @@
+using Microsoft.AspNetCore.Mvc;
+
+[Route("api/[controller]")]
+[ApiController]
+public class ValuesController : ControllerBase
+{
+ [HttpGet]
+ public IAsyncEnumerable Get()
+ {
+ return FetchItems();
+ }
+
+ static async IAsyncEnumerable FetchItems()
+ {
+ for (int i = 1; i <= 10; i++)
+ {
+ await Task.Delay(1000);
+ yield return i;
+ }
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/Item.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/Item.cs
new file mode 100644
index 000000000..6a9851c86
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/Item.cs
@@ -0,0 +1,5 @@
+public class Item
+{
+ public int Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/ItemContext.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/ItemContext.cs
new file mode 100644
index 000000000..1ca706d47
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Data/ItemContext.cs
@@ -0,0 +1,6 @@
+using Microsoft.EntityFrameworkCore;
+
+public class ItemContext(DbContextOptions options) : DbContext(options)
+{
+ public DbSet
- Items => Set
- ();
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/IAsyncEnumerableWithYieldApi.csproj b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/IAsyncEnumerableWithYieldApi.csproj
new file mode 100644
index 000000000..81acd49e9
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/IAsyncEnumerableWithYieldApi.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Program.cs b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Program.cs
new file mode 100644
index 000000000..4bc756cb3
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Program.cs
@@ -0,0 +1,39 @@
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddControllers();
+builder.Services.AddDbContext(options =>
+ options.UseSqlite(builder.Configuration.GetConnectionString("Items")));
+
+var app = builder.Build();
+
+using (var scope = app.Services.CreateScope())
+{
+ var context = scope.ServiceProvider.GetRequiredService();
+ context.Database.EnsureCreated();
+
+ if (!context.Items.Any())
+ {
+ context.Items.AddRange(
+ new Item { Name = "Keyboard" },
+ new Item { Name = "Mouse" },
+ new Item { Name = "Monitor" });
+ context.SaveChanges();
+ }
+}
+
+app.MapControllers();
+
+app.MapGet("/items", () => FetchItems());
+
+app.Run();
+
+static async IAsyncEnumerable FetchItems()
+{
+ for (var i = 1; i <= 10; i++)
+ {
+ await Task.Delay(1000);
+ yield return i;
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Properties/launchSettings.json b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Properties/launchSettings.json
new file mode 100644
index 000000000..03999a45c
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/Properties/launchSettings.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.Development.json b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.Development.json
new file mode 100644
index 000000000..0c208ae91
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.json b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.json
new file mode 100644
index 000000000..be75002d9
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/IAsyncEnumerableWithYieldApi/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "ConnectionStrings": {
+ "Items": "Data Source=items.db"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/SyncYield/Program.cs b/async-csharp/IAsyncEnumerableWithYield/SyncYield/Program.cs
new file mode 100644
index 000000000..7d12714d2
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/SyncYield/Program.cs
@@ -0,0 +1,17 @@
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Start");
+
+foreach (var item in FetchItems())
+{
+ Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {item}");
+}
+
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: End");
+
+static IEnumerable FetchItems()
+{
+ for (int i = 1; i <= 10; i++)
+ {
+ Thread.Sleep(1000);
+ yield return i;
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/SyncYield/SyncYield.csproj b/async-csharp/IAsyncEnumerableWithYield/SyncYield/SyncYield.csproj
new file mode 100644
index 000000000..dfb40caaf
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/SyncYield/SyncYield.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/Program.cs b/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/Program.cs
new file mode 100644
index 000000000..dd64f3e3b
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/Program.cs
@@ -0,0 +1,20 @@
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Start");
+
+foreach (var item in await FetchItems())
+{
+ Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {item}");
+}
+
+Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: End");
+
+static async Task> FetchItems()
+{
+ List Items = new List();
+ for (int i = 1; i <= 10; i++)
+ {
+ await Task.Delay(1000);
+ Items.Add(i);
+ }
+
+ return Items;
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/TaskOfIEnumerable.csproj b/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/TaskOfIEnumerable.csproj
new file mode 100644
index 000000000..dfb40caaf
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/TaskOfIEnumerable/TaskOfIEnumerable.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/async-csharp/IAsyncEnumerableWithYield/Tests/AsAsyncEnumerableUnitTest.cs b/async-csharp/IAsyncEnumerableWithYield/Tests/AsAsyncEnumerableUnitTest.cs
new file mode 100644
index 000000000..e1c7ac848
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/Tests/AsAsyncEnumerableUnitTest.cs
@@ -0,0 +1,53 @@
+using System.Data.Common;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+
+namespace Tests;
+
+[TestClass]
+public class AsAsyncEnumerableUnitTest
+{
+ [TestMethod]
+ public async Task GivenItemsQuery_WhenAsAsyncEnumerableIsCalled_ThenNoCommandRunsUntilEnumeration()
+ {
+ await using var connection = new SqliteConnection("Data Source=:memory:");
+ await connection.OpenAsync();
+
+ var counter = new ReaderCounter();
+ var options = new DbContextOptionsBuilder()
+ .UseSqlite(connection)
+ .AddInterceptors(counter)
+ .Options;
+
+ await using var context = new ItemContext(options);
+ await context.Database.EnsureCreatedAsync();
+ context.Items.AddRange(new Item { Name = "Keyboard" }, new Item { Name = "Mouse" });
+ await context.SaveChangesAsync();
+ counter.Readers = 0;
+
+ var items = context.Items.AsNoTracking().AsAsyncEnumerable();
+ await using var enumerator = items.GetAsyncEnumerator();
+
+ Assert.AreEqual(0, counter.Readers);
+
+ Assert.IsTrue(await enumerator.MoveNextAsync());
+ Assert.AreEqual(1, counter.Readers);
+ Assert.AreEqual("Keyboard", enumerator.Current.Name);
+ }
+
+ private sealed class ReaderCounter : DbCommandInterceptor
+ {
+ public int Readers;
+
+ public override ValueTask> ReaderExecutingAsync(
+ DbCommand command,
+ CommandEventData eventData,
+ InterceptionResult result,
+ CancellationToken cancellationToken = default)
+ {
+ Readers++;
+ return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
+ }
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/Tests/StreamingUnitTest.cs b/async-csharp/IAsyncEnumerableWithYield/Tests/StreamingUnitTest.cs
new file mode 100644
index 000000000..ee1a7a0c1
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/Tests/StreamingUnitTest.cs
@@ -0,0 +1,83 @@
+using System.Diagnostics;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Data.Sqlite;
+
+namespace Tests;
+
+[TestClass]
+public class StreamingUnitTest
+{
+ private static readonly string DatabasePath =
+ Path.Combine(Path.GetTempPath(), $"items-{Guid.NewGuid():N}.db");
+
+ private static WebApplicationFactory _factory = null!;
+
+ [ClassInitialize]
+ public static void ClassInitialize(TestContext _)
+ {
+ _factory = new WebApplicationFactory()
+ .WithWebHostBuilder(builder =>
+ builder.UseSetting("ConnectionStrings:Items", $"Data Source={DatabasePath}"));
+ }
+
+ [ClassCleanup]
+ public static void ClassCleanup()
+ {
+ _factory.Dispose();
+ SqliteConnection.ClearAllPools();
+ File.Delete(DatabasePath);
+ }
+
+ [TestMethod]
+ public async Task GivenValuesController_WhenGetIsCalled_ThenFirstItemArrivesBeforeTheLast()
+ {
+ await AssertStreamsTenItems("/api/values");
+ }
+
+ [TestMethod]
+ public async Task GivenMinimalApiEndpoint_WhenItemsAreRequested_ThenFirstItemArrivesBeforeTheLast()
+ {
+ await AssertStreamsTenItems("/items");
+ }
+
+ [TestMethod]
+ public async Task GivenItemsController_WhenGetItemsIsCalled_ThenEveryRowIsReturned()
+ {
+ using var client = _factory.CreateClient();
+
+ var body = await client.GetStringAsync("/api/items");
+
+ Assert.AreEqual(
+ """[{"id":1,"name":"Keyboard"},{"id":2,"name":"Mouse"},{"id":3,"name":"Monitor"}]""",
+ body);
+ }
+
+ private static async Task AssertStreamsTenItems(string url)
+ {
+ using var client = _factory.CreateClient();
+ var stopwatch = Stopwatch.StartNew();
+
+ using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
+ await using var stream = await response.Content.ReadAsStreamAsync();
+
+ var received = new StringBuilder();
+ var buffer = new byte[64];
+ while (!received.ToString().StartsWith("[1"))
+ {
+ var read = await stream.ReadAsync(buffer);
+ Assert.AreNotEqual(0, read, "The response ended before the first item arrived.");
+ received.Append(Encoding.UTF8.GetString(buffer, 0, read));
+ }
+
+ var firstItemAfter = stopwatch.Elapsed;
+
+ using var reader = new StreamReader(stream);
+ received.Append(await reader.ReadToEndAsync());
+ var lastItemAfter = stopwatch.Elapsed;
+
+ Assert.AreEqual("[1,2,3,4,5,6,7,8,9,10]", received.ToString());
+ Assert.IsLessThan(TimeSpan.FromSeconds(5), firstItemAfter);
+ Assert.IsGreaterThanOrEqualTo(TimeSpan.FromSeconds(9), lastItemAfter);
+ }
+}
diff --git a/async-csharp/IAsyncEnumerableWithYield/Tests/Tests.csproj b/async-csharp/IAsyncEnumerableWithYield/Tests/Tests.csproj
new file mode 100644
index 000000000..3eb168c8a
--- /dev/null
+++ b/async-csharp/IAsyncEnumerableWithYield/Tests/Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+