Browse Source

[genhttp] Update to Version 9 (#9404)

* Switch to .NET 9 but still on old GenHTTP version

* Actually also switch the docker version

* Migrate to GenHTTP 9

* Re-enable real data on fortunes

* Switch to ASP.NET framework

* one should compile before pushing ..

* Another try

* Use other docker file style

* Encode HTML in fortunes

* Assign ID 0 to additional cookie
Andreas Nägeli 10 tháng trước cách đây
mục cha
commit
771117c1d6

+ 36 - 38
frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj

@@ -1,40 +1,38 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
-  
-  <PropertyGroup>
-    
-    <TargetFramework>net8.0</TargetFramework>
-    <LangVersion>10.0</LangVersion>
-    
-    <AssemblyTitle>GenHTTP Benchmarks</AssemblyTitle>
-    <Description>Test suite to be executed with TechEmpower FrameworkBenchmarks.</Description>
-    
-    <StartupObject>Benchmarks.Program</StartupObject>    
-    <OutputType>Exe</OutputType>
-    
-    <ServerGarbageCollection>true</ServerGarbageCollection>
-	  <TieredPGO>true</TieredPGO>
-    
-  </PropertyGroup>
-  
-  <ItemGroup>
-    <None Remove="Resources\Fortunes.html" />
-    <None Remove="Resources\Template.html" />
-  </ItemGroup>
-  
-  <ItemGroup>
-    <EmbeddedResource Include="Resources\Template.html" />
-    <EmbeddedResource Include="Resources\Fortunes.html" />
-  </ItemGroup>
-    
-  <ItemGroup>
-	  
-    <PackageReference Include="GenHTTP.Core" Version="8.5.2" />
-    <PackageReference Include="GenHTTP.Modules.Razor" Version="8.5.0" />
-    <PackageReference Include="GenHTTP.Modules.Webservices" Version="8.5.0" />
-	  
-    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
-    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
-	  
-  </ItemGroup>
-  
+
+    <PropertyGroup>
+
+        <TargetFramework>net9.0</TargetFramework>
+        <LangVersion>13.0</LangVersion>
+        <ImplicitUsings>true</ImplicitUsings>
+        <OutputType>Exe</OutputType>
+
+        <AssemblyTitle>GenHTTP Benchmarks</AssemblyTitle>
+        <Description>Test suite to be executed with TechEmpower FrameworkBenchmarks.</Description>
+
+        <ServerGarbageCollection>true</ServerGarbageCollection>
+        <TieredPGO>true</TieredPGO>
+
+    </PropertyGroup>
+
+    <ItemGroup>
+        <None Remove="Resources\Fortunes.html"/>
+        <None Remove="Resources\Template.html"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <EmbeddedResource Include="Resources\Template.html"/>
+    </ItemGroup>
+
+    <ItemGroup>
+
+        <PackageReference Include="GenHTTP.Core.Kestrel" Version="9.0.0" />
+        <PackageReference Include="GenHTTP.Modules.Razor" Version="8.6.0" />
+        <PackageReference Include="GenHTTP.Modules.Webservices" Version="9.0.0" />
+
+        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
+        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0" />
+
+    </ItemGroup>
+
 </Project>
 </Project>

+ 23 - 32
frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs

@@ -1,52 +1,43 @@
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore;
 
 
-namespace Benchmarks.Model
+namespace Benchmarks.Model;
+
+public sealed class DatabaseContext : DbContext
 {
 {
+    private static DbContextOptions<DatabaseContext> _options;
 
 
-    public sealed class DatabaseContext : DbContext
-    {
-        private static DbContextOptions<DatabaseContext> _Options;
+    private static DbContextOptions<DatabaseContext> _noTrackingOptions;
 
 
-        private static DbContextOptions<DatabaseContext> _NoTrackingOptions;
+    #region Factory
 
 
-        #region Factory
+    public static DatabaseContext Create() => new(_options ??= GetOptions(true));
 
 
-        public static DatabaseContext Create()
-        {
-            return new DatabaseContext(_Options ??= GetOptions(true));
-        }
-
-        public static DatabaseContext CreateNoTracking()
-        {
-            return new DatabaseContext(_NoTrackingOptions ??= GetOptions(false));
-        }
+    public static DatabaseContext CreateNoTracking() => new(_noTrackingOptions ??= GetOptions(false));
 
 
-        private static DbContextOptions<DatabaseContext> GetOptions(bool tracking)
-        {
-            var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
-
-            optionsBuilder.UseNpgsql("Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=256;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4");
+    private static DbContextOptions<DatabaseContext> GetOptions(bool tracking)
+    {
+        var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
 
 
-            if (!tracking)
-            {
-                optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
-            }
+        optionsBuilder.UseNpgsql("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");
 
 
-            return optionsBuilder.Options;
+        if (!tracking)
+        {
+            optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
         }
         }
 
 
-        private DatabaseContext(DbContextOptions options) : base(options) { }
+        return optionsBuilder.Options;
+    }
 
 
-        #endregion
+    private DatabaseContext(DbContextOptions options) : base(options) { }
 
 
-        #region Entities
+    #endregion
 
 
-        public DbSet<World> World { get; set; }
+    #region Entities
 
 
-        public DbSet<Fortune> Fortune { get; set; }
+    public DbSet<World> World { get; set; }
 
 
-        #endregion
+    public DbSet<Fortune> Fortune { get; set; }
 
 
-    }
+    #endregion
 
 
 }
 }

+ 10 - 18
frameworks/CSharp/genhttp/Benchmarks/Model/Fortune.cs

@@ -1,25 +1,17 @@
-using System;
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
 using System.ComponentModel.DataAnnotations.Schema;
 
 
-namespace Benchmarks.Model
-{
-
-    [Table("fortune")]
-    public class Fortune : IComparable<Fortune>, IComparable
-    {
-
-        [Column("id")]
-        public int ID { get; set; }
+namespace Benchmarks.Model;
 
 
-        [Column("message")]
-        [StringLength(2048)]
-        public string Message { get; set; }
-
-        public int CompareTo(object obj) => CompareTo((Fortune)obj);
+[Table("fortune")]
+public class Fortune
+{
 
 
-        public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
+    [Column("id")]
+    public int Id { get; set; }
 
 
-    }
+    [Column("message")]
+    [StringLength(2048)]
+    public string Message { get; set; }
 
 
 }
 }

+ 9 - 12
frameworks/CSharp/genhttp/Benchmarks/Model/World.cs

@@ -1,18 +1,15 @@
 using System.ComponentModel.DataAnnotations.Schema;
 using System.ComponentModel.DataAnnotations.Schema;
 
 
-namespace Benchmarks.Model
-{
-
-    [Table("world")]
-    public class World
-    {
+namespace Benchmarks.Model;
 
 
-        [Column("id")]
-        public int Id { get; set; }
+[Table("world")]
+public class World
+{
 
 
-        [Column("randomnumber")]
-        public int RandomNumber { get; set; }
+    [Column("id")]
+    public int Id { get; set; }
 
 
-    }
+    [Column("randomnumber")]
+    public int RandomNumber { get; set; }
 
 
-}
+}

+ 16 - 31
frameworks/CSharp/genhttp/Benchmarks/Program.cs

@@ -1,35 +1,20 @@
-using GenHTTP.Engine;
-
+using Benchmarks.Tests;
+using Benchmarks.Utilities;
+using GenHTTP.Engine.Kestrel;
 using GenHTTP.Modules.IO;
 using GenHTTP.Modules.IO;
 using GenHTTP.Modules.Layouting;
 using GenHTTP.Modules.Layouting;
 using GenHTTP.Modules.Webservices;
 using GenHTTP.Modules.Webservices;
 
 
-using Benchmarks.Tests;
-using Benchmarks.Utilities;
-
-namespace Benchmarks
-{
-
-    public static class Program
-    {
-
-        public static int Main(string[] args)
-        { 
-            var tests = Layout.Create()
-                              .Add("plaintext", Content.From(Resource.FromString("Hello, World!")))
-                              .Add("fortunes", new FortuneHandlerBuilder())
-                              .AddService<JsonResource>("json")
-                              .AddService<DbResource>("db")
-                              .AddService<QueryResource>("queries")
-                              .AddService<UpdateResource>("updates")
-                              .AddService<CacheResource>("cached-worlds")
-                              .Add(ServerHeader.Create());
-
-            return Host.Create()
-                       .Handler(tests)
-                       .Run();
-        }
-
-    }
-
-}
+var tests = Layout.Create()
+                  .Add("plaintext", Content.From(Resource.FromString("Hello, World!")))
+                  .Add("fortunes", new FortuneHandler())
+                  .AddService<JsonResource>("json")
+                  .AddService<DbResource>("db")
+                  .AddService<QueryResource>("queries")
+                  .AddService<UpdateResource>("updates")
+                  .AddService<CacheResource>("cached-worlds")
+                  .Add(ServerHeader.Create());
+
+return await Host.Create()
+                 .Handler(tests)
+                 .RunAsync();

+ 0 - 7
frameworks/CSharp/genhttp/Benchmarks/Resources/Fortunes.html

@@ -1,7 +0,0 @@
-<table>
-    <tr><th>id</th><th>message</th></tr>
-    @foreach (var cookie in Model.Cookies)
-    {
-    <tr><td>@cookie.ID</td><td>@HttpUtility.HtmlEncode(cookie.Message)</td></tr>
-    }
-</table>

+ 16 - 3
frameworks/CSharp/genhttp/Benchmarks/Resources/Template.html

@@ -1,9 +1,22 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html>
 <html>
 <head>
 <head>
-    <title>@Model.Meta.Title</title>
+    <title>Fortunes</title>
 </head>
 </head>
 <body>
 <body>
-    @Model.Content
+
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    {for cookie in cookies:
+    <tr>
+        <td>{ cookie.id }</td>
+        <td>{ cookie.message }</td>
+    </tr>
+    }
+</table>
+
 </body>
 </body>
-</html>
+</html>

+ 54 - 54
frameworks/CSharp/genhttp/Benchmarks/Tests/CacheResource.cs

@@ -1,84 +1,84 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
+using Benchmarks.Model;
+using GenHTTP.Modules.Webservices;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Caching.Memory;
 
 
-using Benchmarks.Model;
-
-using GenHTTP.Modules.Webservices;
+namespace Benchmarks.Tests;
 
 
-namespace Benchmarks.Tests
+public sealed class CacheResource
 {
 {
+    private static readonly Random Random = new();
 
 
-    public sealed class CacheResource
+    private static readonly MemoryCache Cache = new(new MemoryCacheOptions
     {
     {
-        private static readonly Random _Random = new Random();
-
-        private static readonly MemoryCache _Cache = new MemoryCache(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromMinutes(60) });
-
-        private static readonly object[] _CacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
-
-        public sealed class CacheKey : IEquatable<CacheKey>
-        {
-            private readonly int _value;
-
-            public CacheKey(int value) => _value = value;
+        ExpirationScanFrequency = TimeSpan.FromMinutes(60)
+    });
 
 
-            public bool Equals(CacheKey key) => key._value == _value;
+    private static readonly object[] CacheKeys = Enumerable.Range(0, 10001).Select(i => new CacheKey(i)).ToArray();
 
 
-            public override bool Equals(object obj) => ReferenceEquals(obj, this);
+    [ResourceMethod(":queries")]
+    public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);
 
 
-            public override int GetHashCode() => _value;
+    [ResourceMethod]
+    public async ValueTask<List<World>> GetWorlds(string queries)
+    {
+        var count = 1;
 
 
-            public override string ToString() => _value.ToString();
+        int.TryParse(queries, out count);
 
 
+        if (count < 1)
+        {
+            count = 1;
         }
         }
+        else if (count > 500)
+        {
+            count = 500;
+        }
+
+        var result = new List<World>(count);
 
 
-        [ResourceMethod(":queries")]
-        public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);
+        await using var context = DatabaseContext.CreateNoTracking();
 
 
-        [ResourceMethod]
-        public async ValueTask<List<World>> GetWorlds(string queries)
+        for (var i = 0; i < count; i++)
         {
         {
-            var count = 1;
+            var id = Random.Next(1, 10001);
 
 
-            int.TryParse(queries, out count);
+            var key = CacheKeys[id];
 
 
-            if (count < 1) count = 1;
-            else if (count > 500) count = 500;
+            var data = Cache.Get<World>(key);
 
 
-            var result = new List<World>(count);
+            if (data != null)
+            {
+                result.Add(data);
+            }
+            else
+            {
+                var resolved = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
 
 
-            using var context = DatabaseContext.CreateNoTracking();
+                Cache.Set(key, resolved);
 
 
-            for (var i = 0; i < count; i++)
-            {
-                var id = _Random.Next(1, 10001);
+                result.Add(resolved);
+            }
+        }
 
 
-                var key = _CacheKeys[id];
+        return result;
+    }
 
 
-                var data = _Cache.Get<World>(key);
+    public sealed class CacheKey : IEquatable<CacheKey>
+    {
+        private readonly int _value;
 
 
-                if (data != null)
-                {
-                    result.Add(data);
-                }
-                else
-                {
-                    var resolved = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
+        public CacheKey(int value)
+        {
+            _value = value;
+        }
 
 
-                    _Cache.Set(key, resolved);
+        public bool Equals(CacheKey key) => key._value == _value;
 
 
-                    result.Add(resolved);
-                }
-            }
+        public override bool Equals(object obj) => ReferenceEquals(obj, this);
 
 
-            return result;
-        }
+        public override int GetHashCode() => _value;
 
 
+        public override string ToString() => _value.ToString();
     }
     }
-
 }
 }

+ 10 - 18
frameworks/CSharp/genhttp/Benchmarks/Tests/DbResource.cs

@@ -1,29 +1,21 @@
-using System;
-using System.Threading.Tasks;
-
-using Microsoft.EntityFrameworkCore;
-
+using Benchmarks.Model;
 using GenHTTP.Modules.Webservices;
 using GenHTTP.Modules.Webservices;
+using Microsoft.EntityFrameworkCore;
 
 
-using Benchmarks.Model;
+namespace Benchmarks.Tests;
 
 
-namespace Benchmarks.Tests
+public sealed class DbResource
 {
 {
+    private static readonly Random Random = new();
 
 
-    public sealed class DbResource
+    [ResourceMethod]
+    public async ValueTask<World> GetRandomWorld()
     {
     {
-        private static readonly Random _Random = new();
-
-        [ResourceMethod]
-        public async ValueTask<World> GetRandomWorld()
-        {
-            var id = _Random.Next(1, 10001);
-
-            using var context = DatabaseContext.CreateNoTracking();
+        var id = Random.Next(1, 10001);
 
 
-            return await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
-        }
+        await using var context = DatabaseContext.CreateNoTracking();
 
 
+        return await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
     }
     }
 
 
 }
 }

+ 48 - 79
frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs

@@ -1,116 +1,85 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Linq;
-using System.Web;
-using System.IO;
-
-using Microsoft.EntityFrameworkCore;
-
+using System.Web;
+using Benchmarks.Model;
+using Cottle;
 using GenHTTP.Api.Content;
 using GenHTTP.Api.Content;
-using GenHTTP.Api.Content.Templating;
 using GenHTTP.Api.Protocol;
 using GenHTTP.Api.Protocol;
 
 
 using GenHTTP.Modules.IO;
 using GenHTTP.Modules.IO;
-using GenHTTP.Modules.Razor;
-
-using Benchmarks.Model;
+using GenHTTP.Modules.Pages;
+using GenHTTP.Modules.Pages.Rendering;
 
 
-namespace Benchmarks.Tests
-{
+using Microsoft.EntityFrameworkCore;
 
 
-    #region Factory
+namespace Benchmarks.Tests;
 
 
-    public class FortuneHandlerBuilder : IHandlerBuilder
-    {
+public class FortuneHandler : IHandler
+{
 
 
-        public IHandler Build(IHandler parent)
-        {
-            return new FortuneHandler(parent);
-        }
+    #region Get-/Setters
 
 
-    }
+    private TemplateRenderer Template { get; }
 
 
     #endregion
     #endregion
 
 
-    #region Supporting data structures
+    #region Initialization
 
 
-    public sealed class FortuneModel : BasicModel
+    public FortuneHandler()
     {
     {
+        var resource = Resource.FromAssembly("Template.html").Build();
 
 
-        public List<Fortune> Cookies { get; }
-
-        public FortuneModel(IRequest request, IHandler handler, List<Fortune> cookies) : base(request, handler)
-        {
-            Cookies = cookies;
-        }
-
+        Template = Renderer.From(resource);
     }
     }
 
 
     #endregion
     #endregion
 
 
-    public class FortuneHandler : IHandler, IPageRenderer
-    {
-
-        #region Get-/Setters
-
-        public IHandler Parent { get; }
+    #region Functionality
 
 
-        private IHandler Page { get; }
+    public ValueTask PrepareAsync() => new();
 
 
-        private IRenderer<TemplateModel> Template { get; }
-
-        #endregion
-
-        #region Initialization
-
-        public FortuneHandler(IHandler parent)
+    public async ValueTask<IResponse> HandleAsync(IRequest request)
+    {
+        var data = new Dictionary<Value, Value>
         {
         {
-            Parent = parent;
+            ["cookies"] = Value.FromEnumerable(await GetFortunes())
+        };
 
 
-            Page = ModRazor.Page(Resource.FromAssembly("Fortunes.html"), (r, h) => GetFortunes(r, h))
-                           .Title("Fortunes")
-                           .AddAssemblyReference<HttpUtility>()
-                           .AddUsing("System.Web")
-                           .Build(this);
+        return request.GetPage(await Template.RenderAsync(data)).Build();
+    }
 
 
-            Template = ModRazor.Template<TemplateModel>(Resource.FromAssembly("Template.html")).Build();
-        }
+    private static async ValueTask<List<Value>> GetFortunes()
+    {
+        await using var context = DatabaseContext.CreateNoTracking();
 
 
-        #endregion
+        var fortunes = await context.Fortune.ToListAsync().ConfigureAwait(false);
 
 
-        #region Functionality
+        var result = new List<Value>(fortunes.Count + 1);
 
 
-        public async ValueTask PrepareAsync()
+        foreach (var fortune in fortunes)
         {
         {
-            await Page.PrepareAsync();
-            await Template.PrepareAsync();
+            result.Add(Value.FromDictionary(new Dictionary<Value, Value>()
+            {
+                ["id"] = fortune.Id,
+                ["message"] = HttpUtility.HtmlEncode(fortune.Message)
+            }));
         }
         }
 
 
-        public ValueTask<ulong> CalculateChecksumAsync() => new(17);
-
-        public IAsyncEnumerable<ContentElement> GetContentAsync(IRequest request) => AsyncEnumerable.Empty<ContentElement>();
-
-        public ValueTask<string> RenderAsync(TemplateModel model) => Template.RenderAsync(model);
-
-        public ValueTask RenderAsync(TemplateModel model, Stream target) => Template.RenderAsync(model, target);
-
-        public ValueTask<IResponse> HandleAsync(IRequest request) => Page.HandleAsync(request);
-
-        private static async ValueTask<FortuneModel> GetFortunes(IRequest request, IHandler handler)
+        result.Add(Value.FromDictionary(new Dictionary<Value, Value>()
         {
         {
-            using var context = DatabaseContext.CreateNoTracking();
-
-            var fortunes = await context.Fortune.ToListAsync().ConfigureAwait(false);
-
-            fortunes.Add(new Fortune() { Message = "Additional fortune added at request time." });
-
-            fortunes.Sort();
+            ["id"] = 0,
+            ["message"] = "Additional fortune added at request time."
+        }));
 
 
-            return new FortuneModel(request, handler, fortunes);
-        }
+        result.Sort((one, two) =>
+        {
+            var firstMessage = one.Fields["message"].AsString;
+            var secondMessage = two.Fields["message"].AsString;
 
 
-        #endregion
+            return string.Compare(firstMessage, secondMessage, StringComparison.Ordinal);
+        });
 
 
+        return result;
     }
     }
 
 
+    #endregion
+
 }
 }

+ 11 - 12
frameworks/CSharp/genhttp/Benchmarks/Tests/JsonResource.cs

@@ -1,21 +1,20 @@
 using GenHTTP.Modules.Webservices;
 using GenHTTP.Modules.Webservices;
 
 
-namespace Benchmarks.Tests
-{
+namespace Benchmarks.Tests;
 
 
-    public sealed class JsonResult
-    {
+public sealed class JsonResult
+{
 
 
-        public string Message { get; set; }
+    public string Message { get; set; }
+}
 
 
-    }
+public sealed class JsonResource
+{
 
 
-    public sealed class JsonResource
+    [ResourceMethod]
+    public JsonResult GetMessage() => new()
     {
     {
-
-        [ResourceMethod]
-        public JsonResult GetMessage() => new() { Message = "Hello, World!" };
-
-    }
+        Message = "Hello, World!"
+    };
 
 
 }
 }

+ 27 - 30
frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs

@@ -1,47 +1,44 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
+using Benchmarks.Model;
+using GenHTTP.Modules.Webservices;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore;
 
 
-using Benchmarks.Model;
-
-using GenHTTP.Modules.Webservices;
+namespace Benchmarks.Tests;
 
 
-namespace Benchmarks.Tests
+public sealed class QueryResource
 {
 {
+    private static readonly Random Random = new();
+
+    [ResourceMethod(":queries")]
+    public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);
 
 
-    public sealed class QueryResource
+    [ResourceMethod]
+    public async ValueTask<List<World>> GetWorlds(string queries)
     {
     {
-        private static readonly Random _Random = new Random();
+        var count = 1;
 
 
-        [ResourceMethod(":queries")]
-        public ValueTask<List<World>> GetWorldsFromPath(string queries) => GetWorlds(queries);
+        int.TryParse(queries, out count);
 
 
-        [ResourceMethod]
-        public async ValueTask<List<World>> GetWorlds(string queries)
+        if (count < 1)
         {
         {
-            var count = 1;
-
-            int.TryParse(queries, out count);
-
-            if (count < 1) count = 1;
-            else if (count > 500) count = 500;
-
-            var result = new List<World>(count);
+            count = 1;
+        }
+        else if (count > 500)
+        {
+            count = 500;
+        }
 
 
-            using var context = DatabaseContext.CreateNoTracking();
+        var result = new List<World>(count);
 
 
-            for (int _ = 0; _ < count; _++)
-            {
-                var id = _Random.Next(1, 10001);
+        using var context = DatabaseContext.CreateNoTracking();
 
 
-                result.Add(await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false));
-            }
+        for (var _ = 0; _ < count; _++)
+        {
+            var id = Random.Next(1, 10001);
 
 
-            return result;
+            result.Add(await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false));
         }
         }
 
 
+        return result;
     }
     }
 
 
-}
+}

+ 39 - 41
frameworks/CSharp/genhttp/Benchmarks/Tests/UpdateResource.cs

@@ -1,66 +1,64 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
+using Benchmarks.Model;
+using GenHTTP.Modules.Webservices;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore;
 
 
-using Benchmarks.Model;
+namespace Benchmarks.Tests;
 
 
-using GenHTTP.Modules.Webservices;
-
-namespace Benchmarks.Tests
+public sealed class UpdateResource
 {
 {
+    private static readonly Random Random = new();
+
+    [ResourceMethod(":queries")]
+    public ValueTask<List<World>> UpdateWorldsFromPath(string queries) => UpdateWorlds(queries);
 
 
-    public sealed class UpdateResource
+    [ResourceMethod]
+    public async ValueTask<List<World>> UpdateWorlds(string queries)
     {
     {
-        private static Random _Random = new Random();
+        var count = 1;
 
 
-        [ResourceMethod(":queries")]
-        public ValueTask<List<World>> UpdateWorldsFromPath(string queries) => UpdateWorlds(queries);
+        int.TryParse(queries, out count);
 
 
-        [ResourceMethod]
-        public async ValueTask<List<World>> UpdateWorlds(string queries)
+        if (count < 1)
         {
         {
-            var count = 1;
-
-            int.TryParse(queries, out count);
-
-            if (count < 1) count = 1;
-            else if (count > 500) count = 500;
+            count = 1;
+        }
+        else if (count > 500)
+        {
+            count = 500;
+        }
 
 
-            var result = new List<World>(count);
+        var result = new List<World>(count);
 
 
-            var ids = Enumerable.Range(1, 10000).Select(x => _Random.Next(1, 10001)).Distinct().Take(count).ToArray();
+        var ids = Enumerable.Range(1, 10000).Select(x => Random.Next(1, 10001)).Distinct().Take(count).ToArray();
 
 
-            using (var context = DatabaseContext.Create())
+        using (var context = DatabaseContext.Create())
+        {
+            foreach (var id in ids)
             {
             {
-                foreach (var id in ids)
-                {
-                    var record = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
+                var record = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false);
 
 
-                    var old = record.RandomNumber;
+                var old = record.RandomNumber;
 
 
-                    var current = old;
+                var current = old;
 
 
-                    for (int i = 0; i < 5; i++)
-                    {
-                        current = _Random.Next(1, 10001);
+                for (var i = 0; i < 5; i++)
+                {
+                    current = Random.Next(1, 10001);
 
 
-                        if (current != old) break;
+                    if (current != old)
+                    {
+                        break;
                     }
                     }
+                }
 
 
-                    record.RandomNumber = current;
+                record.RandomNumber = current;
 
 
-                    result.Add(record);
+                result.Add(record);
 
 
-                    await context.SaveChangesAsync();
-                }
+                await context.SaveChangesAsync();
             }
             }
-
-            return result;
         }
         }
 
 
+        return result;
     }
     }
-
-}
+}

+ 4 - 8
frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeader.cs

@@ -1,11 +1,7 @@
-namespace Benchmarks.Utilities
-{
-
-    public static class ServerHeader
-    {
+namespace Benchmarks.Utilities;
 
 
-        public static ServerHeaderConcernBuilder Create() => new ServerHeaderConcernBuilder();
-
-    }
+public static class ServerHeader
+{
 
 
+    public static ServerHeaderConcernBuilder Create() => new();
 }
 }

+ 23 - 36
frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcern.cs

@@ -1,55 +1,42 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-using GenHTTP.Api.Content;
+using GenHTTP.Api.Content;
 using GenHTTP.Api.Protocol;
 using GenHTTP.Api.Protocol;
 
 
-namespace Benchmarks.Utilities
-{
-
-    public sealed class ServerHeaderConcern : IConcern
-    {
+namespace Benchmarks.Utilities;
 
 
-        #region Get-/Setters
+public sealed class ServerHeaderConcern : IConcern
+{
 
 
-        public IHandler Content { get; }
+    #region Initialization
 
 
-        public IHandler Parent { get; }
+    public ServerHeaderConcern(IHandler content)
+    {
+        Content = content;
+    }
 
 
-        #endregion
+    #endregion
 
 
-        #region Initialization
+    #region Get-/Setters
 
 
-        public ServerHeaderConcern(IHandler parent, Func<IHandler, IHandler> contentFactory)
-        {
-            Parent = parent;
-            Content = contentFactory(this);
-        }
+    public IHandler Content { get; }
 
 
-        #endregion
+    #endregion
 
 
-        #region Functionality
+    #region Functionality
 
 
-        public IAsyncEnumerable<ContentElement> GetContentAsync(IRequest request) => Content.GetContentAsync(request);
+    public ValueTask PrepareAsync() => Content.PrepareAsync();
 
 
-        public ValueTask PrepareAsync() => Content.PrepareAsync();
+    public async ValueTask<IResponse> HandleAsync(IRequest request)
+    {
+        var response = await Content.HandleAsync(request);
 
 
-        public async ValueTask<IResponse> HandleAsync(IRequest request)
+        if (response != null)
         {
         {
-            var response = await Content.HandleAsync(request);
-
-            if (response != null)
-            {
-                response.Headers.Add("Server", "TFB");
-            }
-
-            return response;
+            response.Headers.Add("Server", "TFB");
         }
         }
 
 
-        #endregion
-
+        return response;
     }
     }
 
 
+    #endregion
+
 }
 }

+ 4 - 12
frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcernBuilder.cs

@@ -1,18 +1,10 @@
-using System;
+using GenHTTP.Api.Content;
 
 
-using GenHTTP.Api.Content;
+namespace Benchmarks.Utilities;
 
 
-namespace Benchmarks.Utilities
+public sealed class ServerHeaderConcernBuilder : IConcernBuilder
 {
 {
 
 
-    public sealed class ServerHeaderConcernBuilder : IConcernBuilder
-    {
-
-        public IConcern Build(IHandler parent, Func<IHandler, IHandler> contentFactory)
-        {
-            return new ServerHeaderConcern(parent, contentFactory);
-        }
-
-    }
+    public IConcern Build(IHandler content) => new ServerHeaderConcern(content);
 
 
 }
 }

+ 2 - 2
frameworks/CSharp/genhttp/README.md

@@ -6,11 +6,11 @@ See the [GenHTTP website](https://genhttp.org) for more information.
 
 
 **Language**
 **Language**
 
 
-* C# 8.0
+* C# 13.0
 
 
 **Platforms**
 **Platforms**
 
 
-* .NET Core
+* .NET 8/9
 
 
 **Web Servers**
 **Web Servers**
 
 

+ 1 - 1
frameworks/CSharp/genhttp/benchmark_config.json

@@ -17,7 +17,7 @@
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": ".NET",
       "platform": ".NET",
-      "webserver": "GenHTTP",
+      "webserver": "Kestrel",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "GenHTTP",
       "display_name": "GenHTTP",

+ 1 - 1
frameworks/CSharp/genhttp/config.toml

@@ -16,5 +16,5 @@ database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Raw"
 orm = "Raw"
 platform = ".NET"
 platform = ".NET"
-webserver = "GenHTTP"
+webserver = "Kestrel"
 versus = "None"
 versus = "None"

+ 13 - 13
frameworks/CSharp/genhttp/genhttp.dockerfile

@@ -1,19 +1,19 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
-WORKDIR /app
-COPY Benchmarks .
-RUN dotnet publish -c Release -o out
+FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
+WORKDIR /source
 
 
-FROM mcr.microsoft.com/dotnet/runtime:8.0 AS runtime
-ENV DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS 1
+# copy csproj and restore as distinct layers
+COPY Benchmarks/*.csproj .
+RUN dotnet restore -r linux-musl-x64
 
 
-# Full PGO
-ENV DOTNET_TieredPGO 1 
-ENV DOTNET_TC_QuickJitForLoops 1 
-ENV DOTNET_ReadyToRun 0
+# copy and publish app and libraries
+COPY Benchmarks/ .
+RUN dotnet publish -c release -o /app -r linux-musl-x64 --no-restore --self-contained
 
 
+# final stage/image
+FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-alpine
 WORKDIR /app
 WORKDIR /app
-COPY --from=build /app/out ./
+COPY --from=build /app .
 
 
-EXPOSE 8080
+ENTRYPOINT ["./Benchmarks"]
 
 
-ENTRYPOINT ["dotnet", "Benchmarks.dll"]
+EXPOSE 8080