Эх сурвалжийг харах

Update .NET version to 10.0 for CSharp/aspnetcore (#10241)

* Update .NET version to 10.0 for CSharp/aspnetcore

* Update database drivers versions

* Fix updates

* Try older npgsql

* Only run "updates" and "queries"

* Ensure there are no updated ids duplicated

* Prevent collisions

* Update connection string

* Sync code
Sébastien Ros 2 долоо хоног өмнө
parent
commit
e605445baa

+ 1 - 1
frameworks/CSharp/aspnetcore/appsettings.postgresql.json

@@ -1,4 +1,4 @@
 {
 {
-  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=512;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4",
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=18;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000;",
   "Database": "postgresql"
   "Database": "postgresql"
 }
 }

+ 2 - 2
frameworks/CSharp/aspnetcore/aspnetcore-aot.dockerfile

@@ -1,11 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 RUN apt-get update
 RUN apt-get update
 RUN apt-get -yqq install clang zlib1g-dev
 RUN apt-get -yqq install clang zlib1g-dev
 WORKDIR /app
 WORKDIR /app
 COPY src/Platform .
 COPY src/Platform .
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql /p:PublishAot=true /p:OptimizationPreference=Speed /p:GarbageCollectionAdaptationMode=0
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql /p:PublishAot=true /p:OptimizationPreference=Speed /p:GarbageCollectionAdaptationMode=0
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
 ENV URLS=http://+:8080
 ENV URLS=http://+:8080
 
 
 WORKDIR /app
 WORKDIR /app

+ 2 - 2
frameworks/CSharp/aspnetcore/aspnetcore-minimal.dockerfile

@@ -1,9 +1,9 @@
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY src/Minimal .
 COPY src/Minimal .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
 ENV URLS http://+:8080
 ENV URLS http://+:8080
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_ReadyToRun=0
 ENV DOTNET_ReadyToRun=0

+ 2 - 2
frameworks/CSharp/aspnetcore/aspnetcore-mvc.dockerfile

@@ -1,9 +1,9 @@
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY src/Mvc .
 COPY src/Mvc .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
 ENV URLS http://+:8080
 ENV URLS http://+:8080
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_ReadyToRun=0
 ENV DOTNET_ReadyToRun=0

+ 2 - 2
frameworks/CSharp/aspnetcore/aspnetcore-mysql.dockerfile

@@ -1,9 +1,9 @@
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY src/Platform .
 COPY src/Platform .
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=MySqlConnector
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=MySqlConnector
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
 ENV URLS http://+:8080
 ENV URLS http://+:8080
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_ReadyToRun=0
 ENV DOTNET_ReadyToRun=0

+ 2 - 2
frameworks/CSharp/aspnetcore/aspnetcore.dockerfile

@@ -1,9 +1,9 @@
-FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY src/Platform .
 COPY src/Platform .
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql
 RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql
 
 
-FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
 ENV URLS=http://+:8080
 ENV URLS=http://+:8080
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_GCDynamicAdaptationMode=0
 ENV DOTNET_ReadyToRun=0
 ENV DOTNET_ReadyToRun=0

+ 21 - 3
frameworks/CSharp/aspnetcore/src/Minimal/Database/Db.cs

@@ -63,6 +63,9 @@ public class Db
 
 
         var parameters = new Dictionary<string, object>();
         var parameters = new Dictionary<string, object>();
 
 
+        var ids = new int[count];
+        var numbers = new int[count];
+
         await using var db = _dbProviderFactory.CreateConnection();
         await using var db = _dbProviderFactory.CreateConnection();
 
 
         db!.ConnectionString = _connectionString;
         db!.ConnectionString = _connectionString;
@@ -72,18 +75,33 @@ public class Db
         for (var i = 0; i < count; i++)
         for (var i = 0; i < count; i++)
         {
         {
             results[i] = await ReadSingleRow(db);
             results[i] = await ReadSingleRow(db);
+            ids[i] = results[i].Id;
         }
         }
+        Array.Sort(ids);
+
+        // Ensure unique ids by incrementing duplicates
+        for (var i = 1; i < count; i++)
+            if (ids[i] == ids[i - 1])
+                ids[i] = (ids[i] % 10000) + 1;
 
 
         for (var i = 0; i < count; i++)
         for (var i = 0; i < count; i++)
         {
         {
             var randomNumber = Random.Shared.Next(1, 10001);
             var randomNumber = Random.Shared.Next(1, 10001);
-            parameters[$"@Rn_{i}"] = randomNumber;
-            parameters[$"@Id_{i}"] = results[i].Id;
+            if (results[i].RandomNumber == randomNumber)
+            {
+                randomNumber = (randomNumber % 10000) + 1;
+            }
 
 
             results[i].RandomNumber = randomNumber;
             results[i].RandomNumber = randomNumber;
+            numbers[i] = randomNumber;
         }
         }
 
 
-        await db.ExecuteAsync(BatchUpdateString.Query(count), parameters);
+        var update = "UPDATE world w SET randomnumber = u.new_val FROM (SELECT unnest(@ids) as id, unnest(@newValues) as new_val) u WHERE w.id = u.id";
+
+        parameters["ids"] = ids;
+        parameters["newValues"] = numbers;
+
+        await db.ExecuteAsync(update, parameters);
         return results;
         return results;
     }
     }
 
 

+ 4 - 4
frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <TargetFramework>net10.0</TargetFramework>
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
     <ImplicitUsings>enable</ImplicitUsings>
     <LangVersion>latest</LangVersion>
     <LangVersion>latest</LangVersion>
@@ -9,9 +9,9 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Npgsql" Version="9.0.2" />
-    <PackageReference Include="Dapper" Version="2.1.35" />
-    <PackageReference Include="RazorSlices" Version="0.8.1" />
+    <PackageReference Include="Npgsql" Version="10.0.0" />
+    <PackageReference Include="Dapper" Version="2.1.66" />
+    <PackageReference Include="RazorSlices" Version="0.9.5" />
   </ItemGroup>
   </ItemGroup>
 
 
 </Project>
 </Project>

+ 3 - 3
frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <TargetFramework>net10.0</TargetFramework>
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
     <ImplicitUsings>enable</ImplicitUsings>
     <LangVersion>latest</LangVersion>
     <LangVersion>latest</LangVersion>
@@ -9,8 +9,8 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Npgsql" Version="9.0.2" />
-    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
+    <PackageReference Include="Npgsql" Version="10.0.0" />
+    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
   </ItemGroup>
   </ItemGroup>
 
 
 </Project>
 </Project>

+ 0 - 31
frameworks/CSharp/aspnetcore/src/Platform/Data/Random.cs

@@ -1,31 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-namespace PlatformBenchmarks;
-
-public sealed class ConcurrentRandom
-{
-    private static int nextSeed = 0;
-
-    // Random isn't thread safe
-    [ThreadStatic]
-    private static Random _random;
-
-    private static Random Random => _random ?? CreateRandom();
-
-    [MethodImpl(MethodImplOptions.NoInlining)]
-    private static Random CreateRandom()
-    {
-        _random = new Random(Interlocked.Increment(ref nextSeed));
-        return _random;
-    }
-
-    public int Next(int minValue, int maxValue)
-    {
-        return Random.Next(minValue, maxValue);
-    }
-}

+ 28 - 25
frameworks/CSharp/aspnetcore/src/Platform/Data/RawDbMySqlConnector.cs

@@ -17,16 +17,14 @@ namespace PlatformBenchmarks;
 // If you are changing RawDbMySqlConnector.cs, also consider changing RawDbNpgsql.cs.
 // If you are changing RawDbMySqlConnector.cs, also consider changing RawDbNpgsql.cs.
 public sealed class RawDb
 public sealed class RawDb
 {
 {
-    private readonly ConcurrentRandom _random;
     private readonly string _connectionString;
     private readonly string _connectionString;
     private readonly MemoryCache _cache
     private readonly MemoryCache _cache
         = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
         = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
 
 
     private readonly MySqlDataSource _dataSource;
     private readonly MySqlDataSource _dataSource;
 
 
-    public RawDb(ConcurrentRandom random, AppSettings appSettings)
+    public RawDb(AppSettings appSettings)
     {
     {
-        _random = random;
         _connectionString = appSettings.ConnectionString;
         _connectionString = appSettings.ConnectionString;
         _dataSource = new MySqlDataSource(appSettings.ConnectionString);
         _dataSource = new MySqlDataSource(appSettings.ConnectionString);
     }
     }
@@ -46,10 +44,10 @@ public sealed class RawDb
         var result = new CachedWorld[count];
         var result = new CachedWorld[count];
         var cacheKeys = _cacheKeys;
         var cacheKeys = _cacheKeys;
         var cache = _cache;
         var cache = _cache;
-        var random = _random;
+        
         for (var i = 0; i < result.Length; i++)
         for (var i = 0; i < result.Length; i++)
         {
         {
-            var id = random.Next(1, 10001);
+            var id = Random.Shared.Next(1, 10001);
             var key = cacheKeys[id];
             var key = cacheKeys[id];
             if (cache.TryGetValue(key, out var cached))
             if (cache.TryGetValue(key, out var cached))
             {
             {
@@ -80,7 +78,7 @@ public sealed class RawDb
             {
             {
                 result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
                 result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
 
 
-                id = rawdb._random.Next(1, 10001);
+                id = Random.Shared.Next(1, 10001);
                 idParameter.Value = id;
                 idParameter.Value = id;
                 key = cacheKeys[id];
                 key = cacheKeys[id];
             }
             }
@@ -117,24 +115,18 @@ public sealed class RawDb
 
 
         using var connection = await _dataSource.OpenConnectionAsync();
         using var connection = await _dataSource.OpenConnectionAsync();
 
 
-        using var batch = new MySqlBatch(connection);
-
-        for (var i = 0; i < count; i++)
+        // It is not acceptable to execute multiple SELECTs within a single complex query.
+        // It is not acceptable to retrieve all required rows using a SELECT ... WHERE id IN (...) clause.
+        // Pipelining of network traffic between the application and database is permitted.
+        
+        var (queryCmd, queryParameter) = await CreateReadCommandAsync(connection);
+        using (queryCmd)
         {
         {
-            batch.BatchCommands.Add(new MySqlBatchCommand()
+            for (var i = 0; i < results.Length; i++)
             {
             {
-                CommandText = "SELECT id, randomnumber FROM world WHERE id = @id",
-                Parameters = { new MySqlParameter("@id", _random.Next(1, 10001)) }
-            });
-        }
-
-        using var reader = await batch.ExecuteReaderAsync();
-
-        for (var i = 0; i < count; i++)
-        {
-            await reader.ReadAsync();
-            results[i] = new World { Id = reader.GetInt32(0), RandomNumber = reader.GetInt32(1) };
-            await reader.NextResultAsync();
+                queryParameter.Value = Random.Shared.Next(1, 10001);
+                results[i] = await ReadSingleRow(queryCmd);
+            }
         }
         }
 
 
         return results;
         return results;
@@ -144,23 +136,34 @@ public sealed class RawDb
     {
     {
         var results = new World[count];
         var results = new World[count];
 
 
+        var ids = new int[count];
+        for (var i = 0; i < count; i++)
+        {
+            ids[i] = Random.Shared.Next(1, 10001);
+        }
+        Array.Sort(ids);
+
         using var connection = await _dataSource.OpenConnectionAsync();
         using var connection = await _dataSource.OpenConnectionAsync();
 
 
+        // Each row must be selected randomly using one query in the same fashion as the single database query test
+        // Use of IN clauses or similar means to consolidate multiple queries into one operation is not permitted.
+        // Similarly, use of a batch or multiple SELECTs within a single statement are not permitted
         var (queryCmd, queryParameter) = await CreateReadCommandAsync(connection);
         var (queryCmd, queryParameter) = await CreateReadCommandAsync(connection);
         using (queryCmd)
         using (queryCmd)
         {
         {
             for (var i = 0; i < results.Length; i++)
             for (var i = 0; i < results.Length; i++)
             {
             {
+                queryParameter.Value = ids[i];
                 results[i] = await ReadSingleRow(queryCmd);
                 results[i] = await ReadSingleRow(queryCmd);
-                queryParameter.Value = _random.Next(1, 10001);
             }
             }
         }
         }
 
 
+        // MySql doesn't have the unnest function like PostgreSQL, so we have to do a batch update instead
         using (var updateCmd = new MySqlCommand(BatchUpdateString.Query(count), connection))
         using (var updateCmd = new MySqlCommand(BatchUpdateString.Query(count), connection))
         {
         {
             for (var i = 0; i < results.Length; i++)
             for (var i = 0; i < results.Length; i++)
             {
             {
-                var randomNumber = _random.Next(1, 10001);
+                var randomNumber = Random.Shared.Next(1, 10001);
 
 
                 updateCmd.Parameters.AddWithValue($"@Id_{i}", results[i].Id);
                 updateCmd.Parameters.AddWithValue($"@Id_{i}", results[i].Id);
                 updateCmd.Parameters.AddWithValue($"@Random_{i}", randomNumber);
                 updateCmd.Parameters.AddWithValue($"@Random_{i}", randomNumber);
@@ -204,7 +207,7 @@ public sealed class RawDb
     private async Task<(MySqlCommand readCmd, MySqlParameter idParameter)> CreateReadCommandAsync(MySqlConnection connection)
     private async Task<(MySqlCommand readCmd, MySqlParameter idParameter)> CreateReadCommandAsync(MySqlConnection connection)
     {
     {
         var cmd = new MySqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
         var cmd = new MySqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
-        var parameter = new MySqlParameter("@Id", _random.Next(1, 10001));
+        var parameter = new MySqlParameter("@Id", Random.Shared.Next(1, 10001));
 
 
         cmd.Parameters.Add(parameter);
         cmd.Parameters.Add(parameter);
 
 

+ 50 - 42
frameworks/CSharp/aspnetcore/src/Platform/Data/RawDbNpgsql.cs

@@ -10,21 +10,20 @@ using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Caching.Memory;
 using Npgsql;
 using Npgsql;
+using NpgsqlTypes;
 
 
 namespace PlatformBenchmarks;
 namespace PlatformBenchmarks;
 
 
 public sealed class RawDb
 public sealed class RawDb
 {
 {
-    private readonly ConcurrentRandom _random;
     private readonly MemoryCache _cache
     private readonly MemoryCache _cache
         = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
         = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
 
 
     private readonly NpgsqlDataSource _dataSource;
     private readonly NpgsqlDataSource _dataSource;
 
 
-    public RawDb(ConcurrentRandom random, AppSettings appSettings)
+    public RawDb(AppSettings appSettings)
     {
     {
-        _random = random;
-        _dataSource = NpgsqlDataSource.Create(appSettings.ConnectionString);
+        _dataSource = new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).EnableArrays().Build();
     }
     }
 
 
     public async Task<World> LoadSingleQueryRow()
     public async Task<World> LoadSingleQueryRow()
@@ -42,10 +41,10 @@ public sealed class RawDb
         var result = new CachedWorld[count];
         var result = new CachedWorld[count];
         var cacheKeys = _cacheKeys;
         var cacheKeys = _cacheKeys;
         var cache = _cache;
         var cache = _cache;
-        var random = _random;
+
         for (var i = 0; i < result.Length; i++)
         for (var i = 0; i < result.Length; i++)
         {
         {
-            var id = random.Next(1, 10001);
+            var id = Random.Shared.Next(1, 10001);
             var key = cacheKeys[id];
             var key = cacheKeys[id];
             if (cache.TryGetValue(key, out var cached))
             if (cache.TryGetValue(key, out var cached))
             {
             {
@@ -76,7 +75,7 @@ public sealed class RawDb
             {
             {
                 result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
                 result[i] = await rawdb._cache.GetOrCreateAsync(key, create);
 
 
-                id = rawdb._random.Next(1, 10001);
+                id = Random.Shared.Next(1, 10001);
                 idParameter.TypedValue = id;
                 idParameter.TypedValue = id;
                 key = cacheKeys[id];
                 key = cacheKeys[id];
             }
             }
@@ -109,30 +108,18 @@ public sealed class RawDb
 
 
         using var connection = await _dataSource.OpenConnectionAsync();
         using var connection = await _dataSource.OpenConnectionAsync();
 
 
-        using var batch = new NpgsqlBatch(connection)
-        {
-            // Inserts a PG Sync message between each statement in the batch, required for compliance with
-            // TechEmpower general test requirement 7
-            // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview
-            EnableErrorBarriers = true
-        };
-
-        for (var i = 0; i < count; i++)
+        // It is not acceptable to execute multiple SELECTs within a single complex query.
+        // It is not acceptable to retrieve all required rows using a SELECT ... WHERE id IN (...) clause.
+        // Pipelining of network traffic between the application and database is permitted.
+        
+        var (queryCmd, queryParameter) = CreateReadCommand(connection);
+        using (queryCmd)
         {
         {
-            batch.BatchCommands.Add(new()
+            for (var i = 0; i < results.Length; i++)
             {
             {
-                CommandText = "SELECT id, randomnumber FROM world WHERE id = $1",
-                Parameters = { new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) } }
-            });
-        }
-
-        using var reader = await batch.ExecuteReaderAsync();
-
-        for (var i = 0; i < count; i++)
-        {
-            await reader.ReadAsync();
-            results[i] = new World { Id = reader.GetInt32(0), RandomNumber = reader.GetInt32(1) };
-            await reader.NextResultAsync();
+                queryParameter.TypedValue = Random.Shared.Next(1, 10001);
+                results[i] = await ReadSingleRow(queryCmd);
+            }
         }
         }
 
 
         return results;
         return results;
@@ -142,34 +129,55 @@ public sealed class RawDb
     {
     {
         var results = new World[count];
         var results = new World[count];
 
 
-        using var connection = CreateConnection();
-        await connection.OpenAsync();
+        var ids = new int[count];
+        var numbers = new int[count];
+
+        for (var i = 0; i < count; i++)
+        {
+            ids[i] = Random.Shared.Next(1, 10001);
+        }
+        Array.Sort(ids);
+        
+        // Ensure unique ids by incrementing duplicates
+        for (var i = 1; i < count; i++)
+            if (ids[i] == ids[i - 1])
+                ids[i] = (ids[i] % 10000) + 1;
+
+        using var connection = await _dataSource.OpenConnectionAsync();
 
 
+        // Each row must be selected randomly using one query in the same fashion as the single database query test
+        // Use of IN clauses or similar means to consolidate multiple queries into one operation is not permitted.
+        // Similarly, use of a batch or multiple SELECTs within a single statement are not permitted
         var (queryCmd, queryParameter) = CreateReadCommand(connection);
         var (queryCmd, queryParameter) = CreateReadCommand(connection);
         using (queryCmd)
         using (queryCmd)
         {
         {
             for (var i = 0; i < results.Length; i++)
             for (var i = 0; i < results.Length; i++)
             {
             {
+                queryParameter.TypedValue = ids[i];
                 results[i] = await ReadSingleRow(queryCmd);
                 results[i] = await ReadSingleRow(queryCmd);
-                queryParameter.TypedValue = _random.Next(1, 10001);
             }
             }
         }
         }
 
 
-        using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), connection))
+        for (var i = 0; i < count; i++)
         {
         {
-            for (var i = 0; i < results.Length; i++)
+            var randomNumber = Random.Shared.Next(1, 10001);
+            if (results[i].RandomNumber == randomNumber)
             {
             {
-                var randomNumber = _random.Next(1, 10001);
-
-                updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = results[i].Id });
-                updateCmd.Parameters.Add(new NpgsqlParameter<int> { TypedValue = randomNumber });
-
-                results[i].RandomNumber = randomNumber;
+                randomNumber = (randomNumber % 10000) + 1;
             }
             }
 
 
-            await updateCmd.ExecuteNonQueryAsync();
+            results[i].RandomNumber = randomNumber;
+            numbers[i] = randomNumber;
         }
         }
 
 
+        var update = "UPDATE world w SET randomnumber = u.new_val FROM (SELECT unnest($1) as id, unnest($2) as new_val) u WHERE w.id = u.id";
+
+        using var updateCmd = new NpgsqlCommand(update, connection);
+        updateCmd.Parameters.Add(new NpgsqlParameter<int[]> { TypedValue = ids, NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Integer });
+        updateCmd.Parameters.Add(new NpgsqlParameter<int[]> { TypedValue = numbers, NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Integer });
+
+        await updateCmd.ExecuteNonQueryAsync();
+
         return results;
         return results;
     }
     }
 
 
@@ -203,7 +211,7 @@ public sealed class RawDb
     private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
     private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
     {
     {
         var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection);
         var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection);
-        var parameter = new NpgsqlParameter<int> { TypedValue = _random.Next(1, 10001) };
+        var parameter = new NpgsqlParameter<int> { TypedValue = Random.Shared.Next(1, 10001) };
 
 
         cmd.Parameters.Add(parameter);
         cmd.Parameters.Add(parameter);
 
 

+ 5 - 5
frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <TargetFramework>net10.0</TargetFramework>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <IsTestAssetProject>true</IsTestAssetProject>
     <IsTestAssetProject>true</IsTestAssetProject>
     <LangVersion>preview</LangVersion>
     <LangVersion>preview</LangVersion>
@@ -18,10 +18,10 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Npgsql" Version="9.0.2" />
-    <PackageReference Include="MySqlConnector" Version="2.3.7" />
-    <PackageReference Include="Dapper" Version="2.1.35" />
-    <PackageReference Include="RazorSlices" Version="0.8.1" />
+    <PackageReference Include="Npgsql" Version="10.0.0" />
+    <PackageReference Include="MySqlConnector" Version="2.5.0" />
+    <PackageReference Include="Dapper" Version="2.1.66" />
+    <PackageReference Include="RazorSlices" Version="0.9.5" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup Condition="$(PublishAot) == 'true'">
   <ItemGroup Condition="$(PublishAot) == 'true'">

+ 26 - 21
frameworks/CSharp/aspnetcore/src/Platform/Program.cs

@@ -7,6 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
 
 
 namespace PlatformBenchmarks;
 namespace PlatformBenchmarks;
 
 
@@ -42,7 +43,7 @@ public class Program
         await host.RunAsync();
         await host.RunAsync();
     }
     }
 
 
-    public static IWebHost BuildWebHost(string[] args)
+    public static IHost BuildWebHost(string[] args)
     {
     {
         Console.WriteLine($"BuildWebHost()");
         Console.WriteLine($"BuildWebHost()");
         Console.WriteLine($"Args: {string.Join(' ', args)}");
         Console.WriteLine($"Args: {string.Join(' ', args)}");
@@ -59,30 +60,34 @@ public class Program
         var appSettings = config.Get<AppSettings>();
         var appSettings = config.Get<AppSettings>();
         Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}");
         Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}");
 
 
-        BenchmarkApplication.RawDb = new RawDb(new ConcurrentRandom(), appSettings);
+        BenchmarkApplication.RawDb = new RawDb(appSettings);
 
 
-        var hostBuilder = new WebHostBuilder()
-            .UseBenchmarksConfiguration(config)
-            .UseKestrel((context, options) =>
+        var hostBuilder = Host.CreateDefaultBuilder(args)
+            .ConfigureWebHost(webHostBuilder =>
             {
             {
-                var endPoint = context.Configuration.CreateIPEndPoint();
-
-                options.Listen(endPoint, builder =>
+                webHostBuilder
+                    .UseBenchmarksConfiguration(config)
+                    .UseKestrel((context, options) =>
+                    {
+                        var endPoint = context.Configuration.CreateIPEndPoint();
+
+                        options.Listen(endPoint, builder =>
+                        {
+                            builder.UseHttpApplication<BenchmarkApplication>();
+                        });
+                    })
+                    .UseStartup<Startup>();
+
+                webHostBuilder.UseSockets(options =>
                 {
                 {
-                    builder.UseHttpApplication<BenchmarkApplication>();
-                });
-            })
-            .UseStartup<Startup>();
-
-        hostBuilder.UseSockets(options =>
-        {
-            options.WaitForDataBeforeAllocatingBuffer = false;
+                    options.WaitForDataBeforeAllocatingBuffer = false;
 
 
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-            {
-                options.UnsafePreferInlineScheduling = true;
-            }
-        });
+                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                    {
+                        options.UnsafePreferInlineScheduling = true;
+                    }
+                });
+            });
 
 
         var host = hostBuilder.Build();
         var host = hostBuilder.Build();