Browse Source

Appmpowerv8.0.5: using native AOT dll instead of exe (#9214)

* New Npgsql security update

* Middleware alternative

* NativeAOT calls

* hellloWorld -> helloWorld

* Reduce external calls

* JsonMiddleware

* JsonMessage built in NativeAOT

* mac dylibs

* Size and content separate

* Small corrections

* JsonMessage

* JsonMessage struct

* Rename nativeAOT -> appMpowerAot

* Working single query, but slow

* Odbc test

* dockerfile changes

* Try to get correct capitalization

* 2nd part trial for correct capitalization

* Dbms and DbProvider parameterization

* linux-x64

* NativeMethods

* Kestrel header

* Header http

* Using marshalling and out parameter

* Header changes

* FreeUnmanagedPointer

* Correction single query

* JsonMessage pointer handling

* Native last methods for JsonMessage and hello world

* Sync db read

* Create new json message

* Rename appMpowerAot -> appMpower.Orm

* Fortunes

* Synchronous fortunes read

* Server settings

* Empty line

* More correct special characters length

* Remove ADO and InternalConnection

* Synchronous only

* Cleanup

* Name changes

* Multiple queries

* Cleanup

* Alternative native methods

* MultipleUpdates

* odbcCommand dictionary

* keyed commands

* Refactoring

* Remove comments

* First MySQL trial

* MySQL odbc

* zip file mariadb connector

* Better download ?

* dockerfile cleanup

* charset

* Cached queries

* Improved caching

* Improved caching

* Removal unnecessary packages

* Cleanup project

* appMpower cleanup

* appMpower.Orm cleanup

* Too much cleanup

---------

Co-authored-by: LLT21 <>
LLT21 1 year ago
parent
commit
9c9b8678aa
65 changed files with 1728 additions and 2303 deletions
  1. 0 17
      frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile
  2. 51 0
      frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile
  3. 7 1
      frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile
  4. 6 0
      frameworks/CSharp/appmpower/appmpower.dockerfile
  5. 4 4
      frameworks/CSharp/appmpower/benchmark_config.json
  6. 2 2
      frameworks/CSharp/appmpower/config.toml
  7. 10 0
      frameworks/CSharp/appmpower/odbcinst.ini
  8. 0 15
      frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs
  9. 0 200
      frameworks/CSharp/appmpower/src/Data/DbConnection.cs
  10. 0 67
      frameworks/CSharp/appmpower/src/Data/DbConnections.cs
  11. 0 17
      frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs
  12. 0 16
      frameworks/CSharp/appmpower/src/Data/InternalConnection.cs
  13. 0 117
      frameworks/CSharp/appmpower/src/HttpApplication.cs
  14. 0 7
      frameworks/CSharp/appmpower/src/JsonMessage.cs
  15. 0 58
      frameworks/CSharp/appmpower/src/Kestrel/Json.cs
  16. 0 38
      frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs
  17. 0 25
      frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs
  18. 0 257
      frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs
  19. 0 32
      frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs
  20. 0 62
      frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs
  21. 0 140
      frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs
  22. 0 520
      frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs
  23. 0 67
      frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs
  24. 0 24
      frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs
  25. 0 59
      frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs
  26. 0 42
      frameworks/CSharp/appmpower/src/Program.cs
  27. 0 331
      frameworks/CSharp/appmpower/src/RawDb.cs
  28. 10 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs
  29. 42 63
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs
  30. 153 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs
  31. 61 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs
  32. 8 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs
  33. 21 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs
  34. 9 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs
  35. 66 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs
  36. 4 17
      frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs
  37. 19 37
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs
  38. 0 2
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs
  39. 54 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs
  40. 144 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs
  41. 1 1
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs
  42. 1 3
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs
  43. 1 1
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs
  44. 257 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs
  45. 9 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs
  46. 4 2
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs
  47. 24 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs
  48. 42 55
      frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj
  49. 7 0
      frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs
  50. 120 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs
  51. 58 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs
  52. 64 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs
  53. 62 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs
  54. 62 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs
  55. 50 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs
  56. 57 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs
  57. 62 0
      frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs
  58. 44 0
      frameworks/CSharp/appmpower/src/appMpower/Program.cs
  59. 1 1
      frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs
  60. 3 3
      frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs
  61. 56 0
      frameworks/CSharp/appmpower/src/appMpower/Startup.cs
  62. 35 0
      frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj
  63. 9 0
      frameworks/CSharp/appmpower/src/appMpower/appsettings.json
  64. 0 0
      frameworks/CSharp/appmpower/src/appMpower/nuget.config
  65. 28 0
      frameworks/CSharp/appmpower/src/src.sln

+ 0 - 17
frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile

@@ -1,17 +0,0 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
-RUN apt-get update
-RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
-
-WORKDIR /app
-COPY src .
-RUN dotnet publish -c Release -o out /p:Driver=ado
-
-# Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
-
-WORKDIR /app
-COPY --from=build /app/out ./
-
-EXPOSE 8080
-
-ENTRYPOINT ["./appMpower"]

+ 51 - 0
frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile

@@ -0,0 +1,51 @@
+FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
+RUN apt-get update
+RUN apt-get -yqq install clang zlib1g-dev
+RUN apt-get update
+
+WORKDIR /app
+COPY src .
+RUN dotnet publish -c Release -o out /p:Database=mysql
+
+# Construct the actual image that will run
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
+
+RUN apt-get update
+# The following installs standard versions unixodbc and pgsqlodbc
+# unixodbc still needs to be installed even if compiled locally
+RUN apt-get install -y unixodbc wget curl
+RUN apt-get update
+
+WORKDIR /odbc
+
+RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb/libm* /usr/lib/
+RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb /usr/local/lib/mariadb
+RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+#TODOLOCAL
+#RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+#RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+#RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb/libm* /usr/lib/
+#RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb /usr/local/lib/mariadb
+#RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+
+ENV PATH=/usr/local/unixODBC/bin:$PATH
+
+WORKDIR /etc/
+COPY odbcinst.ini .
+
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
+ENV ASPNETCORE_URLS http://+:8080
+WORKDIR /app
+COPY --from=build /app/out ./
+
+RUN cp /usr/lib/libm* /app
+
+EXPOSE 8080
+
+ENTRYPOINT ["./appMpower"]

+ 7 - 1
frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile

@@ -4,7 +4,7 @@ RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 
 WORKDIR /app
 COPY src .
-RUN dotnet publish -c Release -o out /p:Driver=odbc
+RUN dotnet publish -c Release -o out /p:Database=postgresql
 
 # Construct the actual image that will run
 FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
@@ -18,6 +18,12 @@ ENV PATH=/usr/local/unixODBC/bin:$PATH
 WORKDIR /etc/
 COPY odbcinst.ini .
 
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
+ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 COPY --from=build /app/out ./
 

+ 6 - 0
frameworks/CSharp/appmpower/appmpower.dockerfile

@@ -4,11 +4,17 @@ RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 
 WORKDIR /app
 COPY src .
+#RUN dotnet publish appMpower/appMpower.csproj -c Release -o out
 RUN dotnet publish -c Release -o out
 
 # Construct the actual image that will run
 FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
 
+ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 COPY --from=build /app/out ./
 

+ 4 - 4
frameworks/CSharp/appmpower/benchmark_config.json

@@ -39,11 +39,11 @@
         "webserver": "Kestrel",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "appMpower [aot-no-reflection,odbc]",
+        "display_name": "appMpower [aot-no-reflection,pg,odbc]",
         "notes": "",
         "versus": "aspnetcore-minimal"
       },
-      "ado-pg": {
+      "odbc-my": {
         "db_url": "/db",
         "query_url": "/queries?c=",
         "update_url": "/updates?c=",
@@ -52,7 +52,7 @@
         "port": 8080,
         "approach": "Realistic",
         "classification": "Platform",
-        "database": "Postgres",
+        "database": "MySQL",
         "framework": "appmpower",
         "language": "C#",
         "orm": "Raw",
@@ -61,7 +61,7 @@
         "webserver": "Kestrel",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "appMpower [aot-no-reflection,ado]",
+        "display_name": "appMpower [aot-no-reflection,my,odbc]",
         "notes": "",
         "versus": "aspnetcore-minimal"
       }

+ 2 - 2
frameworks/CSharp/appmpower/config.toml

@@ -30,7 +30,7 @@ platform = ".NET"
 webserver = "Kestrel"
 versus = "aspnetcore-minimal"
 
-[ado-pg]
+[odbc-my]
 urls.db = "/db"
 urls.query = "/queries?c="
 urls.update = "/updates?c="
@@ -38,7 +38,7 @@ urls.fortune = "/fortunes"
 urls.cached_query = "/cached-worlds?c="
 approach = "Realistic"
 classification = "Micro"
-database = "Postgres"
+database = "MySQL"
 database_os = "Linux"
 os = "Linux"
 orm = "Raw"

+ 10 - 0
frameworks/CSharp/appmpower/odbcinst.ini

@@ -5,6 +5,7 @@ Pooling=0
 
 [ODBC Drivers]
 PostgreSQL = Installed
+MariaDB = Installed
 
 ;
 ;  odbcinst.ini
@@ -15,7 +16,16 @@ Description=ODBC for PostgreSQL
 ; in version 08.x. Note that the library can also be installed under an other
 ; path than /usr/local/lib/ following your installation.
 ; This is the standard location used by apt-get install -y unixodbc
+;ON SERVER
 Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
+;TODOLOCAL: ON MAC
+;Driver =/usr/lib/aarch64-linux-gnu/odbc/psqlodbcw.so
+
 ;Driver =/usr/local/pgsqlodbc/lib/psqlodbcw.so
 Threading = 0
 CPTimeout = 0
+
+[MariaDB]
+Description=MariaDB ODBC for MySQL
+Driver = /usr/lib/libmaodbc.so
+Threading   = 0

+ 0 - 15
frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs

@@ -1,15 +0,0 @@
-using System.Text.Json;
-
-namespace appMpower
-{
-   public class CachedWorldSerializer : Kestrel.IJsonSerializer<CachedWorld>
-   {
-      public void Serialize(Utf8JsonWriter utf8JsonWriter, CachedWorld world)
-      {
-         utf8JsonWriter.WriteStartObject();
-         utf8JsonWriter.WriteNumber("id", world.Id);
-         utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
-         utf8JsonWriter.WriteEndObject();
-      }
-   }
-}

+ 0 - 200
frameworks/CSharp/appmpower/src/Data/DbConnection.cs

@@ -1,200 +0,0 @@
-using System.Collections.Concurrent;
-using System.Data;
-using System.Threading.Tasks;
-
-namespace appMpower.Data
-{
-   public class DbConnection : IDbConnection
-   {
-      private string _connectionString;
-      internal InternalConnection _internalConnection;
-
-      public DbConnection()
-      {
-         _connectionString = DbProviderFactory.ConnectionString;
-      }
-
-      public DbConnection(string connectionString)
-      {
-         _connectionString = connectionString;
-      }
-
-      internal ConcurrentDictionary<string, DbCommand> DbCommands
-      {
-         get
-         {
-            return _internalConnection.DbCommands;
-         }
-         set
-         {
-            _internalConnection.DbCommands = value;
-         }
-      }
-
-      public short Number
-      {
-         get
-         {
-            return _internalConnection.Number;
-         }
-         set
-         {
-            _internalConnection.Number = value;
-         }
-      }
-
-      public IDbConnection Connection
-      {
-         get
-         {
-            return _internalConnection.DbConnection;
-         }
-         set
-         {
-            _internalConnection.DbConnection = value;
-         }
-      }
-
-      public string ConnectionString
-      {
-         get
-         {
-            return _internalConnection.DbConnection.ConnectionString;
-         }
-         set
-         {
-            _internalConnection.DbConnection.ConnectionString = value;
-         }
-      }
-
-      public int ConnectionTimeout
-      {
-         get
-         {
-            return _internalConnection.DbConnection.ConnectionTimeout;
-         }
-      }
-
-      public string Database
-      {
-         get
-         {
-            return _internalConnection.DbConnection.Database;
-         }
-      }
-
-      public ConnectionState State
-      {
-         get
-         {
-            if (_internalConnection is null) return ConnectionState.Closed;
-            return _internalConnection.DbConnection.State;
-         }
-      }
-
-      public IDbTransaction BeginTransaction()
-      {
-         return _internalConnection.DbConnection.BeginTransaction();
-      }
-
-      public IDbTransaction BeginTransaction(IsolationLevel il)
-      {
-         return _internalConnection.DbConnection.BeginTransaction(il);
-      }
-
-      public void ChangeDatabase(string databaseName)
-      {
-         _internalConnection.DbConnection.ChangeDatabase(databaseName);
-      }
-
-      public void Close()
-      {
-         _internalConnection.DbConnection.Close();
-      }
-
-      public async Task CloseAsync()
-      {
-         await (_internalConnection.DbConnection as System.Data.Common.DbConnection).CloseAsync();
-      }
-
-      public IDbCommand CreateCommand()
-      {
-         return _internalConnection.DbConnection.CreateCommand();
-      }
-
-      public void Open()
-      {
-         if (_internalConnection.DbConnection.State == ConnectionState.Closed)
-         {
-            _internalConnection.DbConnection.Open();
-         }
-      }
-
-      public void Dispose()
-      {
-#if ADO
-         _internalConnection.DbConnection.Dispose();
-         _internalConnection.Dispose();
-#else
-         DbConnections.Release(_internalConnection);
-#endif
-      }
-
-      public async Task OpenAsync()
-      {
-#if ADO && POSTGRESQL
-         _internalConnection = new(); 
-         _internalConnection.DbConnection = new Npgsql.NpgsqlConnection(_connectionString);
-#else
-         if (_internalConnection is null)
-         {
-            _internalConnection = await DbConnections.GetConnection(_connectionString);
-         }
-#endif
-
-         if (_internalConnection.DbConnection.State == ConnectionState.Closed)
-         {
-            await (_internalConnection.DbConnection as System.Data.Common.DbConnection).OpenAsync();
-         }
-      }
-
-      internal DbCommand GetCommand(string commandText, CommandType commandType, DbCommand dbCommand)
-      {
-#if ADO
-         dbCommand.Command = _internalConnection.DbConnection.CreateCommand();
-         dbCommand.Command.CommandText = commandText;
-         dbCommand.Command.CommandType = commandType;
-         dbCommand.DbConnection = this;
-#else
-         DbCommand internalCommand;
-
-         if (_internalConnection.DbCommands.TryRemove(commandText, out internalCommand))
-         {
-            dbCommand.Command = internalCommand.Command;
-            dbCommand.DbConnection = internalCommand.DbConnection;
-         }
-         else
-         {
-            dbCommand.Command = _internalConnection.DbConnection.CreateCommand();
-            dbCommand.Command.CommandText = commandText;
-            dbCommand.Command.CommandType = commandType;
-            dbCommand.DbConnection = this;
-
-            //For non odbc drivers like Npgsql which do not support Prepare
-            dbCommand.Command.Prepare();
-
-            //Console.WriteLine("prepare pool connection: " + this._internalConnection.Number + " for command " + _internalConnection.DbCommands.Count);
-         }
-#endif
-
-         return dbCommand;
-      }
-
-      public void ReleaseCommand(DbCommand dbCommand)
-      {
-#if !ADO
-         _internalConnection.DbCommands.TryAdd(dbCommand.CommandText, dbCommand);
-#endif
-      }
-   }
-}

+ 0 - 67
frameworks/CSharp/appmpower/src/Data/DbConnections.cs

@@ -1,67 +0,0 @@
-using System.Collections.Concurrent;
-using System.Threading.Tasks;
-
-namespace appMpower.Data
-{
-   public static class DbConnections
-   {
-      private static bool _connectionsCreated = false;
-      private static short _createdConnections = 0;
-      private static short _maxConnections = 500;
-
-      private static ConcurrentStack<InternalConnection> _stack = new();
-      private static ConcurrentQueue<TaskCompletionSource<InternalConnection>> _waitingQueue = new();
-
-      public static async Task<InternalConnection> GetConnection(string connectionString)
-      {
-         InternalConnection internalConnection = null;
-
-         if (_connectionsCreated)
-         {
-            if (!_stack.TryPop(out internalConnection))
-            {
-               internalConnection = await GetDbConnectionAsync();
-            }
-
-            return internalConnection;
-         }
-         else
-         {
-            internalConnection = new InternalConnection();
-            internalConnection.DbConnection = new System.Data.Odbc.OdbcConnection(connectionString);
-
-            _createdConnections++;
-
-            if (_createdConnections == _maxConnections) _connectionsCreated = true;
-
-            internalConnection.Number = _createdConnections;
-            internalConnection.DbCommands = new ConcurrentDictionary<string, DbCommand>();
-            //Console.WriteLine("opened connection number: " + dbConnection.Number);
-
-            return internalConnection;
-         }
-      }
-
-      public static Task<InternalConnection> GetDbConnectionAsync()
-      {
-         var taskCompletionSource = new TaskCompletionSource<InternalConnection>(TaskCreationOptions.RunContinuationsAsynchronously);
-
-         _waitingQueue.Enqueue(taskCompletionSource);
-         return taskCompletionSource.Task;
-      }
-
-      public static void Release(InternalConnection internalConnection)
-      {
-         TaskCompletionSource<InternalConnection> taskCompletionSource;
-
-         if (_waitingQueue.TryDequeue(out taskCompletionSource))
-         {
-            taskCompletionSource.SetResult(internalConnection);
-         }
-         else
-         {
-            _stack.Push(internalConnection);
-         }
-      }
-   }
-}

+ 0 - 17
frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs

@@ -1,17 +0,0 @@
-using System.Data;
-
-namespace appMpower.Data
-{
-   public static class DbProviderFactory
-   {
-#if MYSQL
-      public const string ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; 
-#elif ADO
-      public const string ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=0;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; 
-      //public const string ConnectionString = "Server=localhost;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; 
-#else
-      public const string ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
-      //public const string ConnectionString = "Driver={PostgreSQL};Server=localhost;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
-#endif
-   }
-}

+ 0 - 16
frameworks/CSharp/appmpower/src/Data/InternalConnection.cs

@@ -1,16 +0,0 @@
-using System.Collections.Concurrent;
-using System.Data;
-
-namespace appMpower.Data
-{
-   public class InternalConnection : System.IDisposable
-   {
-      public short Number { get; set; }
-      public IDbConnection DbConnection { get; set; }
-      public ConcurrentDictionary<string, DbCommand> DbCommands { get; set; }
-
-      public void Dispose()
-      {
-      }
-   }
-}

+ 0 - 117
frameworks/CSharp/appmpower/src/HttpApplication.cs

@@ -1,117 +0,0 @@
-using System;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting.Server;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Features;
-using appMpower.Kestrel;
-
-namespace appMpower
-{
-   public class HttpApplication : IHttpApplication<IFeatureCollection>
-   {
-      public static readonly byte[] _plainText = Encoding.UTF8.GetBytes("Hello, World!");
-      private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
-      private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
-      private readonly static CachedWorldSerializer _cachedWorldSerializer = new CachedWorldSerializer();
-
-      public IFeatureCollection CreateContext(IFeatureCollection featureCollection)
-      {
-         return featureCollection;
-      }
-
-      public async Task ProcessRequestAsync(IFeatureCollection featureCollection)
-      {
-         var request = featureCollection as IHttpRequestFeature;
-         var httpResponse = featureCollection as IHttpResponseFeature;
-         var httpResponseBody = featureCollection as IHttpResponseBodyFeature;
-
-         PathString pathString = request.Path;
-
-         if (pathString.HasValue)
-         {
-            int pathStringLength = pathString.Value.Length;
-            string pathStringStart = pathString.Value.Substring(1, 1);
-
-            if (pathStringLength == 10 && pathStringStart == "p")
-            {
-               //await PlainText.RenderAsync(httpResponse.Headers, httpResponseBody.Writer, _plainText);
-               PlainText.Render(httpResponse.Headers, httpResponseBody, _plainText);
-               return;
-            }
-            else if (pathStringLength == 5 && pathStringStart == "j")
-            {
-               Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, new JsonMessage { message = "Hello, World!" }, _jsonMessageSerializer);
-               return;
-            }
-            else if (pathStringLength == 3 && pathStringStart == "d")
-            {
-               Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadSingleQueryRow(), _worldSerializer);
-               return;
-            }
-            else if (pathStringLength == 8 && pathStringStart == "q")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-#if ADO
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleQueriesRows(count), _worldSerializer);
-#else
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.ReadMultipleRows(count), _worldSerializer);
-#endif
-
-               return;
-            }
-            else if (pathStringLength == 9 && pathStringStart == "f")
-            {
-               await FortunesView.Render(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadFortunesRows());
-               return;
-            }
-            else if (pathStringLength == 8 && pathStringStart == "u")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleUpdatesRows(count), _worldSerializer);
-               return;
-            }
-            else if (pathStringLength == 14 && pathStringStart == "c")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadCachedQueries(count), _cachedWorldSerializer);
-               return;
-            }
-         }
-      }
-
-      public void DisposeContext(IFeatureCollection featureCollection, Exception exception)
-      {
-      }
-   }
-}

+ 0 - 7
frameworks/CSharp/appmpower/src/JsonMessage.cs

@@ -1,7 +0,0 @@
-namespace appMpower
-{
-   public struct JsonMessage
-   {
-      public string message { get; set; }
-   }
-}

+ 0 - 58
frameworks/CSharp/appmpower/src/Kestrel/Json.cs

@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using System.Text.Json;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-
-namespace appMpower.Kestrel
-{
-   public static class Json
-   {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", "k");
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", "application/json");
-
-      [ThreadStatic]
-      private static Utf8JsonWriter _utf8JsonWriter;
-
-      public static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
-      {
-         SkipValidation = true
-      };
-
-      public static void RenderOne<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T t, IJsonSerializer<T> jsonSerializer)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
-         Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
-         utf8JsonWriter.Reset(pipeWriter);
-
-         jsonSerializer.Serialize(utf8JsonWriter, t);
-         utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
-      }
-
-      public static void RenderMany<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T[] tArray, IJsonSerializer<T> jsonSerializer)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
-         Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
-         utf8JsonWriter.Reset(pipeWriter);
-
-         utf8JsonWriter.WriteStartArray();
-
-         foreach (var t in tArray)
-         {
-            jsonSerializer.Serialize(utf8JsonWriter, t);
-         }
-
-         utf8JsonWriter.WriteEndArray();
-         utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
-      }
-   }
-}

+ 0 - 38
frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs

@@ -1,38 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Microsoft.AspNetCore.Http.Features;
-
-namespace appMpower.Kestrel
-{
-   public static class PlainText
-   {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/plain"));
-
-      public static async Task RenderAsync(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, ReadOnlyMemory<byte> utf8String)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8String.Length.ToString()));
-
-         await pipeWriter.WriteAsync(utf8String);
-         pipeWriter.Complete();
-      }
-
-      public static void Render(IHeaderDictionary headerDictionary, IHttpResponseBodyFeature httpResponseBodyFeature, byte[] utf8String)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-         int length = utf8String.Length;
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", length.ToString()));
-
-         httpResponseBodyFeature.Stream.Write(utf8String, 0, length);
-      }
-   }
-}

+ 0 - 25
frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs

@@ -1,25 +0,0 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace appMpower.Kestrel
-{
-   public class ServiceProvider : ISupportRequiredService, IServiceProvider
-   {
-      public object GetRequiredService(Type serviceType)
-      {
-         return GetService(serviceType);
-      }
-
-      public object GetService(Type serviceType)
-      {
-         if (serviceType == typeof(ILoggerFactory))
-         {
-            return NullLoggerFactory.Instance;
-         }
-
-         return null;
-      }
-   }
-}

+ 0 - 257
frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs

@@ -1,257 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry : ICacheEntry
-   {
-      private static readonly Action<object> ExpirationCallback = ExpirationTokensExpired;
-
-      private readonly MemoryCache _cache;
-
-      private CacheEntryTokens _tokens; // might be null if user is not using the tokens or callbacks
-      private TimeSpan? _absoluteExpirationRelativeToNow;
-      private TimeSpan? _slidingExpiration;
-      private long? _size;
-      private CacheEntry _previous; // this field is not null only before the entry is added to the cache and tracking is enabled
-      private object _value;
-      private CacheEntryState _state;
-
-      internal CacheEntry(object key, MemoryCache memoryCache)
-      {
-         Key = key ?? throw new ArgumentNullException(nameof(key));
-         _cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
-         _previous = memoryCache.TrackLinkedCacheEntries ? CacheEntryHelper.EnterScope(this) : null;
-         _state = new CacheEntryState(CacheItemPriority.Normal);
-      }
-
-      /// <summary>
-      /// Gets or sets an absolute expiration date for the cache entry.
-      /// </summary>
-      public DateTimeOffset? AbsoluteExpiration { get; set; }
-
-      /// <summary>
-      /// Gets or sets an absolute expiration time, relative to now.
-      /// </summary>
-      public TimeSpan? AbsoluteExpirationRelativeToNow
-      {
-         get => _absoluteExpirationRelativeToNow;
-         set
-         {
-            // this method does not set AbsoluteExpiration as it would require calling Clock.UtcNow twice:
-            // once here and once in MemoryCache.SetEntry
-
-            if (value <= TimeSpan.Zero)
-            {
-               throw new ArgumentOutOfRangeException(
-                   nameof(AbsoluteExpirationRelativeToNow),
-                   value,
-                   "The relative expiration value must be positive.");
-            }
-
-            _absoluteExpirationRelativeToNow = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed.
-      /// This will not extend the entry lifetime beyond the absolute expiration (if set).
-      /// </summary>
-      public TimeSpan? SlidingExpiration
-      {
-         get => _slidingExpiration;
-         set
-         {
-            if (value <= TimeSpan.Zero)
-            {
-               throw new ArgumentOutOfRangeException(
-                   nameof(SlidingExpiration),
-                   value,
-                   "The sliding expiration value must be positive.");
-            }
-
-            _slidingExpiration = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets the <see cref="IChangeToken"/> instances which cause the cache entry to expire.
-      /// </summary>
-      public IList<IChangeToken> ExpirationTokens => GetOrCreateTokens().ExpirationTokens;
-
-      /// <summary>
-      /// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache.
-      /// </summary>
-      public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks => GetOrCreateTokens().PostEvictionCallbacks;
-
-      /// <summary>
-      /// Gets or sets the priority for keeping the cache entry in the cache during a
-      /// memory pressure triggered cleanup. The default is <see cref="CacheItemPriority.Normal"/>.
-      /// </summary>
-      public CacheItemPriority Priority { get => _state.Priority; set => _state.Priority = value; }
-
-      /// <summary>
-      /// Gets or sets the size of the cache entry value.
-      /// </summary>
-      public long? Size
-      {
-         get => _size;
-         set
-         {
-            if (value < 0)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
-            }
-
-            _size = value;
-         }
-      }
-
-      public object Key { get; private set; }
-
-      public object Value
-      {
-         get => _value;
-         set
-         {
-            _value = value;
-            _state.IsValueSet = true;
-         }
-      }
-
-      internal DateTimeOffset LastAccessed { get; set; }
-
-      internal EvictionReason EvictionReason { get => _state.EvictionReason; private set => _state.EvictionReason = value; }
-
-      public void Dispose()
-      {
-         if (!_state.IsDisposed)
-         {
-            _state.IsDisposed = true;
-
-            if (_cache.TrackLinkedCacheEntries)
-            {
-               CacheEntryHelper.ExitScope(this, _previous);
-            }
-
-            // Don't commit or propagate options if the CacheEntry Value was never set.
-            // We assume an exception occurred causing the caller to not set the Value successfully,
-            // so don't use this entry.
-            if (_state.IsValueSet)
-            {
-               _cache.SetEntry(this);
-
-               if (_previous != null && CanPropagateOptions())
-               {
-                  PropagateOptions(_previous);
-               }
-            }
-
-            _previous = null; // we don't want to root unnecessary objects
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      internal bool CheckExpired(in DateTimeOffset now)
-          => _state.IsExpired
-              || CheckForExpiredTime(now)
-              || (_tokens != null && _tokens.CheckForExpiredTokens(this));
-
-      internal void SetExpired(EvictionReason reason)
-      {
-         if (EvictionReason == EvictionReason.None)
-         {
-            EvictionReason = reason;
-         }
-         _state.IsExpired = true;
-         _tokens?.DetachTokens();
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      private bool CheckForExpiredTime(in DateTimeOffset now)
-      {
-         if (!AbsoluteExpiration.HasValue && !_slidingExpiration.HasValue)
-         {
-            return false;
-         }
-
-         return FullCheck(now);
-
-         bool FullCheck(in DateTimeOffset offset)
-         {
-            if (AbsoluteExpiration.HasValue && AbsoluteExpiration.Value <= offset)
-            {
-               SetExpired(EvictionReason.Expired);
-               return true;
-            }
-
-            if (_slidingExpiration.HasValue
-                && (offset - LastAccessed) >= _slidingExpiration)
-            {
-               SetExpired(EvictionReason.Expired);
-               return true;
-            }
-
-            return false;
-         }
-      }
-
-      internal void AttachTokens() => _tokens?.AttachTokens(this);
-
-      private static void ExpirationTokensExpired(object obj)
-      {
-         // start a new thread to avoid issues with callbacks called from RegisterChangeCallback
-         Task.Factory.StartNew(state =>
-         {
-            var entry = (CacheEntry)state;
-            entry.SetExpired(EvictionReason.TokenExpired);
-            entry._cache.EntryExpired(entry);
-         }, obj, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-      }
-
-      internal void InvokeEvictionCallbacks() => _tokens?.InvokeEvictionCallbacks(this);
-
-      // this simple check very often allows us to avoid expensive call to PropagateOptions(CacheEntryHelper.Current)
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      internal bool CanPropagateOptions() => (_tokens != null && _tokens.CanPropagateTokens()) || AbsoluteExpiration.HasValue;
-
-      internal void PropagateOptions(CacheEntry parent)
-      {
-         if (parent == null)
-         {
-            return;
-         }
-
-         // Copy expiration tokens and AbsoluteExpiration to the cache entries hierarchy.
-         // We do this regardless of it gets cached because the tokens are associated with the value we'll return.
-         _tokens?.PropagateTokens(parent);
-
-         if (AbsoluteExpiration.HasValue)
-         {
-            if (!parent.AbsoluteExpiration.HasValue || AbsoluteExpiration < parent.AbsoluteExpiration)
-            {
-               parent.AbsoluteExpiration = AbsoluteExpiration;
-            }
-         }
-      }
-
-      private CacheEntryTokens GetOrCreateTokens()
-      {
-         if (_tokens != null)
-         {
-            return _tokens;
-         }
-
-         CacheEntryTokens result = new CacheEntryTokens();
-         return Interlocked.CompareExchange(ref _tokens, result, null) ?? result;
-      }
-   }
-}

+ 0 - 32
frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs

@@ -1,32 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Threading;
-
-namespace appMpower.Memory
-{
-   internal static class CacheEntryHelper
-   {
-      private static readonly AsyncLocal<CacheEntry> _current = new AsyncLocal<CacheEntry>();
-
-      internal static CacheEntry Current
-      {
-         get => _current.Value;
-         private set => _current.Value = value;
-      }
-
-      internal static CacheEntry EnterScope(CacheEntry current)
-      {
-         CacheEntry previous = Current;
-         Current = current;
-         return previous;
-      }
-
-      internal static void ExitScope(CacheEntry current, CacheEntry previous)
-      {
-         Debug.Assert(Current == current, "Entries disposed in invalid order");
-         Current = previous;
-      }
-   }
-}

+ 0 - 62
frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs

@@ -1,62 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry
-   {
-      // this type exists just to reduce CacheEntry size by replacing many enum & boolean fields with one of a size of Int32
-      private struct CacheEntryState
-      {
-         private byte _flags;
-         private byte _evictionReason;
-         private byte _priority;
-
-         internal CacheEntryState(CacheItemPriority priority) : this() => _priority = (byte)priority;
-
-         internal bool IsDisposed
-         {
-            get => ((Flags)_flags & Flags.IsDisposed) != 0;
-            set => SetFlag(Flags.IsDisposed, value);
-         }
-
-         internal bool IsExpired
-         {
-            get => ((Flags)_flags & Flags.IsExpired) != 0;
-            set => SetFlag(Flags.IsExpired, value);
-         }
-
-         internal bool IsValueSet
-         {
-            get => ((Flags)_flags & Flags.IsValueSet) != 0;
-            set => SetFlag(Flags.IsValueSet, value);
-         }
-
-         internal EvictionReason EvictionReason
-         {
-            get => (EvictionReason)_evictionReason;
-            set => _evictionReason = (byte)value;
-         }
-
-         internal CacheItemPriority Priority
-         {
-            get => (CacheItemPriority)_priority;
-            set => _priority = (byte)value;
-         }
-
-         private void SetFlag(Flags option, bool value) => _flags = (byte)(value ? (_flags | (byte)option) : (_flags & ~(byte)option));
-
-         [Flags]
-         private enum Flags : byte
-         {
-            Default = 0,
-            IsValueSet = 1 << 0,
-            IsExpired = 1 << 1,
-            IsDisposed = 1 << 2,
-         }
-      }
-   }
-}

+ 0 - 140
frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs

@@ -1,140 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry
-   {
-      // this type exists just to reduce average CacheEntry size
-      // which typically is not using expiration tokens or callbacks
-      private sealed class CacheEntryTokens
-      {
-         private List<IChangeToken> _expirationTokens;
-         private List<IDisposable> _expirationTokenRegistrations;
-         private List<PostEvictionCallbackRegistration> _postEvictionCallbacks; // this is not really related to tokens, but was moved here to shrink typical CacheEntry size
-
-         internal List<IChangeToken> ExpirationTokens => _expirationTokens ??= new List<IChangeToken>();
-         internal List<PostEvictionCallbackRegistration> PostEvictionCallbacks => _postEvictionCallbacks ??= new List<PostEvictionCallbackRegistration>();
-
-         internal void AttachTokens(CacheEntry cacheEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  for (int i = 0; i < _expirationTokens.Count; i++)
-                  {
-                     IChangeToken expirationToken = _expirationTokens[i];
-                     if (expirationToken.ActiveChangeCallbacks)
-                     {
-                        _expirationTokenRegistrations ??= new List<IDisposable>(1);
-                        IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, cacheEntry);
-                        _expirationTokenRegistrations.Add(registration);
-                     }
-                  }
-               }
-            }
-         }
-
-         internal bool CheckForExpiredTokens(CacheEntry cacheEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               for (int i = 0; i < _expirationTokens.Count; i++)
-               {
-                  IChangeToken expiredToken = _expirationTokens[i];
-                  if (expiredToken.HasChanged)
-                  {
-                     cacheEntry.SetExpired(EvictionReason.TokenExpired);
-                     return true;
-                  }
-               }
-            }
-            return false;
-         }
-
-         internal bool CanPropagateTokens() => _expirationTokens != null;
-
-         internal void PropagateTokens(CacheEntry parentEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  lock (parentEntry.GetOrCreateTokens())
-                  {
-                     foreach (IChangeToken expirationToken in _expirationTokens)
-                     {
-                        parentEntry.AddExpirationToken(expirationToken);
-                     }
-                  }
-               }
-            }
-         }
-
-         internal void DetachTokens()
-         {
-            // _expirationTokenRegistrations is not checked for null, because AttachTokens might initialize it under lock
-            // instead we are checking for _expirationTokens, because if they are not null, then _expirationTokenRegistrations might also be not null
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  List<IDisposable> registrations = _expirationTokenRegistrations;
-                  if (registrations != null)
-                  {
-                     _expirationTokenRegistrations = null;
-                     for (int i = 0; i < registrations.Count; i++)
-                     {
-                        IDisposable registration = registrations[i];
-                        registration.Dispose();
-                     }
-                  }
-               }
-            }
-         }
-
-         internal void InvokeEvictionCallbacks(CacheEntry cacheEntry)
-         {
-            if (_postEvictionCallbacks != null)
-            {
-               Task.Factory.StartNew(state => InvokeCallbacks((CacheEntry)state), cacheEntry,
-                   CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-            }
-         }
-
-         private static void InvokeCallbacks(CacheEntry entry)
-         {
-            List<PostEvictionCallbackRegistration> callbackRegistrations = Interlocked.Exchange(ref entry._tokens._postEvictionCallbacks, null);
-
-            if (callbackRegistrations == null)
-            {
-               return;
-            }
-
-            for (int i = 0; i < callbackRegistrations.Count; i++)
-            {
-               PostEvictionCallbackRegistration registration = callbackRegistrations[i];
-
-               try
-               {
-                  registration.EvictionCallback?.Invoke(entry.Key, entry.Value, entry.EvictionReason, registration.State);
-               }
-               catch (Exception e)
-               {
-                  // This will be invoked on a background thread, don't let it throw.
-                  entry._cache._logger.LogError(e, "EvictionCallback invoked failed");
-               }
-            }
-         }
-      }
-   }
-}

+ 0 - 520
frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs

@@ -1,520 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Internal;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   /// <summary>
-   /// An implementation of <see cref="IMemoryCache"/> using a dictionary to
-   /// store its entries.
-   /// </summary>
-   public class MemoryCache : IMemoryCache
-   {
-      internal readonly ILogger _logger;
-
-      private readonly MemoryCacheOptions _options;
-      private readonly ConcurrentDictionary<object, CacheEntry> _entries;
-
-      private long _cacheSize;
-      private bool _disposed;
-      private DateTimeOffset _lastExpirationScan;
-
-      /// <summary>
-      /// Creates a new <see cref="MemoryCache"/> instance.
-      /// </summary>
-      /// <param name="optionsAccessor">The options of the cache.</param>
-      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor)
-          : this(optionsAccessor, NullLoggerFactory.Instance) { }
-
-      /// <summary>
-      /// Creates a new <see cref="MemoryCache"/> instance.
-      /// </summary>
-      /// <param name="optionsAccessor">The options of the cache.</param>
-      /// <param name="loggerFactory">The factory used to create loggers.</param>
-      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor, ILoggerFactory loggerFactory)
-      {
-         if (optionsAccessor == null)
-         {
-            throw new ArgumentNullException(nameof(optionsAccessor));
-         }
-
-         if (loggerFactory == null)
-         {
-            throw new ArgumentNullException(nameof(loggerFactory));
-         }
-
-         _options = optionsAccessor.Value;
-         //_logger = loggerFactory.CreateLogger<MemoryCache>();
-         _logger = loggerFactory.CreateLogger("MemoryCache");
-
-         _entries = new ConcurrentDictionary<object, CacheEntry>();
-
-         if (_options.Clock == null)
-         {
-            _options.Clock = new SystemClock();
-         }
-
-         _lastExpirationScan = _options.Clock.UtcNow;
-         TrackLinkedCacheEntries = _options.TrackLinkedCacheEntries; // we store the setting now so it's consistent for entire MemoryCache lifetime
-      }
-
-      /// <summary>
-      /// Cleans up the background collection events.
-      /// </summary>
-      ~MemoryCache() => Dispose(false);
-
-      /// <summary>
-      /// Gets the count of the current entries for diagnostic purposes.
-      /// </summary>
-      public int Count => _entries.Count;
-
-      // internal for testing
-      internal long Size { get => Interlocked.Read(ref _cacheSize); }
-
-      internal bool TrackLinkedCacheEntries { get; }
-
-      private ICollection<KeyValuePair<object, CacheEntry>> EntriesCollection => _entries;
-
-      /// <inheritdoc />
-      public ICacheEntry CreateEntry(object key)
-      {
-         CheckDisposed();
-         ValidateCacheKey(key);
-
-         return new CacheEntry(key, this);
-      }
-
-      internal void SetEntry(CacheEntry entry)
-      {
-         if (_disposed)
-         {
-            // No-op instead of throwing since this is called during CacheEntry.Dispose
-            return;
-         }
-
-         if (_options.SizeLimit.HasValue && !entry.Size.HasValue)
-         {
-            //throw new InvalidOperationException(SR.Format(SR.CacheEntryHasEmptySize, nameof(entry.Size), nameof(_options.SizeLimit)));
-            throw new InvalidOperationException();
-         }
-
-         DateTimeOffset utcNow = _options.Clock.UtcNow;
-
-         DateTimeOffset? absoluteExpiration = null;
-         if (entry.AbsoluteExpirationRelativeToNow.HasValue)
-         {
-            absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow;
-         }
-         else if (entry.AbsoluteExpiration.HasValue)
-         {
-            absoluteExpiration = entry.AbsoluteExpiration;
-         }
-
-         // Applying the option's absolute expiration only if it's not already smaller.
-         // This can be the case if a dependent cache entry has a smaller value, and
-         // it was set by cascading it to its parent.
-         if (absoluteExpiration.HasValue)
-         {
-            if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration.Value < entry.AbsoluteExpiration.Value)
-            {
-               entry.AbsoluteExpiration = absoluteExpiration;
-            }
-         }
-
-         // Initialize the last access timestamp at the time the entry is added
-         entry.LastAccessed = utcNow;
-
-         if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
-         {
-            priorEntry.SetExpired(EvictionReason.Replaced);
-         }
-
-         bool exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry);
-
-         if (!entry.CheckExpired(utcNow) && !exceedsCapacity)
-         {
-            bool entryAdded = false;
-
-            if (priorEntry == null)
-            {
-               // Try to add the new entry if no previous entries exist.
-               entryAdded = _entries.TryAdd(entry.Key, entry);
-            }
-            else
-            {
-               // Try to update with the new entry if a previous entries exist.
-               entryAdded = _entries.TryUpdate(entry.Key, entry, priorEntry);
-
-               if (entryAdded)
-               {
-                  if (_options.SizeLimit.HasValue)
-                  {
-                     // The prior entry was removed, decrease the by the prior entry's size
-                     Interlocked.Add(ref _cacheSize, -priorEntry.Size.Value);
-                  }
-               }
-               else
-               {
-                  // The update will fail if the previous entry was removed after retrival.
-                  // Adding the new entry will succeed only if no entry has been added since.
-                  // This guarantees removing an old entry does not prevent adding a new entry.
-                  entryAdded = _entries.TryAdd(entry.Key, entry);
-               }
-            }
-
-            if (entryAdded)
-            {
-               entry.AttachTokens();
-            }
-            else
-            {
-               if (_options.SizeLimit.HasValue)
-               {
-                  // Entry could not be added, reset cache size
-                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-               }
-               entry.SetExpired(EvictionReason.Replaced);
-               entry.InvokeEvictionCallbacks();
-            }
-
-            if (priorEntry != null)
-            {
-               priorEntry.InvokeEvictionCallbacks();
-            }
-         }
-         else
-         {
-            if (exceedsCapacity)
-            {
-               // The entry was not added due to overcapacity
-               entry.SetExpired(EvictionReason.Capacity);
-
-               TriggerOvercapacityCompaction();
-            }
-            else
-            {
-               if (_options.SizeLimit.HasValue)
-               {
-                  // Entry could not be added due to being expired, reset cache size
-                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-               }
-            }
-
-            entry.InvokeEvictionCallbacks();
-            if (priorEntry != null)
-            {
-               RemoveEntry(priorEntry);
-            }
-         }
-
-         StartScanForExpiredItemsIfNeeded(utcNow);
-      }
-
-      /// <inheritdoc />
-      public bool TryGetValue(object key, out object result)
-      {
-         ValidateCacheKey(key);
-         CheckDisposed();
-
-         DateTimeOffset utcNow = _options.Clock.UtcNow;
-
-         if (_entries.TryGetValue(key, out CacheEntry entry))
-         {
-            // Check if expired due to expiration tokens, timers, etc. and if so, remove it.
-            // Allow a stale Replaced value to be returned due to concurrent calls to SetExpired during SetEntry.
-            if (!entry.CheckExpired(utcNow) || entry.EvictionReason == EvictionReason.Replaced)
-            {
-               entry.LastAccessed = utcNow;
-               result = entry.Value;
-
-               if (TrackLinkedCacheEntries && entry.CanPropagateOptions())
-               {
-                  // When this entry is retrieved in the scope of creating another entry,
-                  // that entry needs a copy of these expiration tokens.
-                  entry.PropagateOptions(CacheEntryHelper.Current);
-               }
-
-               StartScanForExpiredItemsIfNeeded(utcNow);
-
-               return true;
-            }
-            else
-            {
-               // TODO: For efficiency queue this up for batch removal
-               RemoveEntry(entry);
-            }
-         }
-
-         StartScanForExpiredItemsIfNeeded(utcNow);
-
-         result = null;
-         return false;
-      }
-
-      /// <inheritdoc />
-      public void Remove(object key)
-      {
-         ValidateCacheKey(key);
-
-         CheckDisposed();
-         if (_entries.TryRemove(key, out CacheEntry entry))
-         {
-            if (_options.SizeLimit.HasValue)
-            {
-               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-            }
-
-            entry.SetExpired(EvictionReason.Removed);
-            entry.InvokeEvictionCallbacks();
-         }
-
-         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
-      }
-
-      private void RemoveEntry(CacheEntry entry)
-      {
-         if (EntriesCollection.Remove(new KeyValuePair<object, CacheEntry>(entry.Key, entry)))
-         {
-            if (_options.SizeLimit.HasValue)
-            {
-               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-            }
-            entry.InvokeEvictionCallbacks();
-         }
-      }
-
-      internal void EntryExpired(CacheEntry entry)
-      {
-         // TODO: For efficiency consider processing these expirations in batches.
-         RemoveEntry(entry);
-         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
-      }
-
-      // Called by multiple actions to see how long it's been since we last checked for expired items.
-      // If sufficient time has elapsed then a scan is initiated on a background task.
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      private void StartScanForExpiredItemsIfNeeded(DateTimeOffset utcNow)
-      {
-         if (_options.ExpirationScanFrequency < utcNow - _lastExpirationScan)
-         {
-            ScheduleTask(utcNow);
-         }
-
-         void ScheduleTask(DateTimeOffset utcNow)
-         {
-            _lastExpirationScan = utcNow;
-            Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this,
-                CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-         }
-      }
-
-      private static void ScanForExpiredItems(MemoryCache cache)
-      {
-         DateTimeOffset now = cache._lastExpirationScan = cache._options.Clock.UtcNow;
-
-         foreach (KeyValuePair<object, CacheEntry> item in cache._entries)
-         {
-            CacheEntry entry = item.Value;
-
-            if (entry.CheckExpired(now))
-            {
-               cache.RemoveEntry(entry);
-            }
-         }
-      }
-
-      private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry)
-      {
-         if (!_options.SizeLimit.HasValue)
-         {
-            return false;
-         }
-
-         long newSize = 0L;
-         for (int i = 0; i < 100; i++)
-         {
-            long sizeRead = Interlocked.Read(ref _cacheSize);
-            newSize = sizeRead + entry.Size.Value;
-
-            if (newSize < 0 || newSize > _options.SizeLimit)
-            {
-               // Overflow occurred, return true without updating the cache size
-               return true;
-            }
-
-            if (sizeRead == Interlocked.CompareExchange(ref _cacheSize, newSize, sizeRead))
-            {
-               return false;
-            }
-         }
-
-         return true;
-      }
-
-      private void TriggerOvercapacityCompaction()
-      {
-         _logger.LogDebug("Overcapacity compaction triggered");
-
-         // Spawn background thread for compaction
-         ThreadPool.QueueUserWorkItem(s => OvercapacityCompaction((MemoryCache)s), this);
-      }
-
-      private static void OvercapacityCompaction(MemoryCache cache)
-      {
-         long currentSize = Interlocked.Read(ref cache._cacheSize);
-
-         cache._logger.LogDebug($"Overcapacity compaction executing. Current size {currentSize}");
-
-         double? lowWatermark = cache._options.SizeLimit * (1 - cache._options.CompactionPercentage);
-         if (currentSize > lowWatermark)
-         {
-            cache.Compact(currentSize - (long)lowWatermark, entry => entry.Size.Value);
-         }
-
-         cache._logger.LogDebug($"Overcapacity compaction executed. New size {Interlocked.Read(ref cache._cacheSize)}");
-      }
-
-      /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy:
-      /// 1. Remove all expired items.
-      /// 2. Bucket by CacheItemPriority.
-      /// 3. Least recently used objects.
-      /// ?. Items with the soonest absolute expiration.
-      /// ?. Items with the soonest sliding expiration.
-      /// ?. Larger objects - estimated by object graph size, inaccurate.
-      public void Compact(double percentage)
-      {
-         int removalCountTarget = (int)(_entries.Count * percentage);
-         Compact(removalCountTarget, _ => 1);
-      }
-
-      private void Compact(long removalSizeTarget, Func<CacheEntry, long> computeEntrySize)
-      {
-         var entriesToRemove = new List<CacheEntry>();
-         var lowPriEntries = new List<CacheEntry>();
-         var normalPriEntries = new List<CacheEntry>();
-         var highPriEntries = new List<CacheEntry>();
-         long removedSize = 0;
-
-         // Sort items by expired & priority status
-         DateTimeOffset now = _options.Clock.UtcNow;
-         foreach (KeyValuePair<object, CacheEntry> item in _entries)
-         {
-            CacheEntry entry = item.Value;
-            if (entry.CheckExpired(now))
-            {
-               entriesToRemove.Add(entry);
-               removedSize += computeEntrySize(entry);
-            }
-            else
-            {
-               switch (entry.Priority)
-               {
-                  case CacheItemPriority.Low:
-                     lowPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.Normal:
-                     normalPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.High:
-                     highPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.NeverRemove:
-                     break;
-                  default:
-                     throw new NotSupportedException("Not implemented: " + entry.Priority);
-               }
-            }
-         }
-
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, lowPriEntries);
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, normalPriEntries);
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, highPriEntries);
-
-         foreach (CacheEntry entry in entriesToRemove)
-         {
-            RemoveEntry(entry);
-         }
-
-         // Policy:
-         // 1. Least recently used objects.
-         // ?. Items with the soonest absolute expiration.
-         // ?. Items with the soonest sliding expiration.
-         // ?. Larger objects - estimated by object graph size, inaccurate.
-         static void ExpirePriorityBucket(ref long removedSize, long removalSizeTarget, Func<CacheEntry, long> computeEntrySize, List<CacheEntry> entriesToRemove, List<CacheEntry> priorityEntries)
-         {
-            // Do we meet our quota by just removing expired entries?
-            if (removalSizeTarget <= removedSize)
-            {
-               // No-op, we've met quota
-               return;
-            }
-
-            // Expire enough entries to reach our goal
-            // TODO: Refine policy
-
-            // LRU
-            priorityEntries.Sort((e1, e2) => e1.LastAccessed.CompareTo(e2.LastAccessed));
-            foreach (CacheEntry entry in priorityEntries)
-            {
-               entry.SetExpired(EvictionReason.Capacity);
-               entriesToRemove.Add(entry);
-               removedSize += computeEntrySize(entry);
-
-               if (removalSizeTarget <= removedSize)
-               {
-                  break;
-               }
-            }
-         }
-      }
-
-      public void Dispose()
-      {
-         Dispose(true);
-      }
-
-      protected virtual void Dispose(bool disposing)
-      {
-         if (!_disposed)
-         {
-            if (disposing)
-            {
-               GC.SuppressFinalize(this);
-            }
-
-            _disposed = true;
-         }
-      }
-
-      private void CheckDisposed()
-      {
-         if (_disposed)
-         {
-            Throw();
-         }
-
-         static void Throw() => throw new ObjectDisposedException(typeof(MemoryCache).FullName);
-      }
-
-      private static void ValidateCacheKey(object key)
-      {
-         if (key == null)
-         {
-            Throw();
-         }
-
-         static void Throw() => throw new ArgumentNullException(nameof(key));
-      }
-   }
-}

+ 0 - 67
frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs

@@ -1,67 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using Microsoft.Extensions.Internal;
-using Microsoft.Extensions.Options;
-
-namespace appMpower.Memory
-{
-   public class MemoryCacheOptions : IOptions<MemoryCacheOptions>
-   {
-      private long? _sizeLimit;
-      private double _compactionPercentage = 0.05;
-
-      public ISystemClock Clock { get; set; }
-
-      /// <summary>
-      /// Gets or sets the minimum length of time between successive scans for expired items.
-      /// </summary>
-      public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1);
-
-      /// <summary>
-      /// Gets or sets the maximum size of the cache.
-      /// </summary>
-      public long? SizeLimit
-      {
-         get => _sizeLimit;
-         set
-         {
-            if (value < 0)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
-            }
-
-            _sizeLimit = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets the amount to compact the cache by when the maximum size is exceeded.
-      /// </summary>
-      public double CompactionPercentage
-      {
-         get => _compactionPercentage;
-         set
-         {
-            if (value < 0 || value > 1)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be between 0 and 1 inclusive.");
-            }
-
-            _compactionPercentage = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets whether to track linked entries. Disabled by default.
-      /// </summary>
-      /// <remarks>Prior to .NET 7 this feature was always enabled.</remarks>
-      public bool TrackLinkedCacheEntries { get; set; }
-
-      MemoryCacheOptions IOptions<MemoryCacheOptions>.Value
-      {
-         get { return this; }
-      }
-   }
-}

+ 0 - 24
frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs

@@ -1,24 +0,0 @@
-using System;
-
-namespace PlatformBenchmarks
-{
-   public sealed class CacheKey : IEquatable<CacheKey>
-   {
-      private readonly int _value;
-
-      public CacheKey(int value)
-          => _value = value;
-
-      public bool Equals(CacheKey key)
-          => key._value == _value;
-
-      public override bool Equals(object obj)
-          => ReferenceEquals(obj, this);
-
-      public override int GetHashCode()
-          => _value;
-
-      public override string ToString()
-          => _value.ToString();
-   }
-}

+ 0 - 59
frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs

@@ -1,59 +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.Text;
-
-namespace PlatformBenchmarks
-{
-   internal static class StringBuilderCache
-   {
-      private const int DefaultCapacity = 1386;
-      private const int MaxBuilderSize = DefaultCapacity * 3;
-
-      [ThreadStatic]
-      private static StringBuilder t_cachedInstance;
-
-      /// <summary>Get a StringBuilder for the specified capacity.</summary>
-      /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
-      public static StringBuilder Acquire(int capacity = DefaultCapacity)
-      {
-         if (capacity <= MaxBuilderSize)
-         {
-            StringBuilder sb = t_cachedInstance;
-            if (capacity < DefaultCapacity)
-            {
-               capacity = DefaultCapacity;
-            }
-
-            if (sb != null)
-            {
-               // Avoid stringbuilder block fragmentation by getting a new StringBuilder
-               // when the requested size is larger than the current capacity
-               if (capacity <= sb.Capacity)
-               {
-                  t_cachedInstance = null;
-                  sb.Clear();
-                  return sb;
-               }
-            }
-         }
-         return new StringBuilder(capacity);
-      }
-
-      public static void Release(StringBuilder sb)
-      {
-         if (sb.Capacity <= MaxBuilderSize)
-         {
-            t_cachedInstance = sb;
-         }
-      }
-
-      public static string GetStringAndRelease(StringBuilder sb)
-      {
-         string result = sb.ToString();
-         Release(sb);
-         return result;
-      }
-   }
-}

+ 0 - 42
frameworks/CSharp/appmpower/src/Program.cs

@@ -1,42 +0,0 @@
-using System;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting.Server.Features;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
-using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-
-namespace appMpower
-{
-   class Program
-   {
-      static async Task Main(string[] args)
-      {
-         var socketTransportOptions = new SocketTransportOptions();
-         var socketTransportFactory = new SocketTransportFactory(Options.Create(socketTransportOptions), NullLoggerFactory.Instance);
-         var kestrelServerOptions = new KestrelServerOptions();
-
-         kestrelServerOptions.Listen(IPAddress.Any, 8080);
-         kestrelServerOptions.AllowSynchronousIO = true; 
-         kestrelServerOptions.AddServerHeader = false;
-
-         using var kestrelServer = new KestrelServer(Options.Create(kestrelServerOptions), socketTransportFactory, NullLoggerFactory.Instance);
-
-         await kestrelServer.StartAsync(new HttpApplication(), CancellationToken.None);
-
-         Console.WriteLine("Listening on:");
-
-         foreach (var address in kestrelServer.Features.Get<IServerAddressesFeature>().Addresses)
-         {
-            Console.WriteLine(" - " + address);
-         }
-
-         Console.WriteLine("Process CTRL+C to quit");
-         var wh = new ManualResetEventSlim();
-         Console.CancelKeyPress += (sender, e) => wh.Set();
-         wh.Wait();
-      }
-   }
-}

+ 0 - 331
frameworks/CSharp/appmpower/src/RawDb.cs

@@ -1,331 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Caching.Memory;
-using appMpower.Data; 
-using PlatformBenchmarks;
-
-namespace appMpower
-{
-   public static class RawDb
-   {
-      private const int MaxBatch = 500;
-
-#if ADO      
-      private static ConcurrentRandom _random = new ConcurrentRandom();
-#else
-      private static Random _random = new Random();
-#endif
-
-      private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
-
-      private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
-
-      private static readonly appMpower.Memory.MemoryCache _cache = new appMpower.Memory.MemoryCache(
-            new appMpower.Memory.MemoryCacheOptions()
-            {
-               ExpirationScanFrequency = TimeSpan.FromMinutes(60)
-            });
-
-      public static async Task<World> LoadSingleQueryRow()
-      {
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, _) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            var world = await ReadSingleRow(dbCommand);
-
-            return world;
-         }
-      }
-
-      public static async Task<World[]> LoadMultipleQueriesRows(int count)
-      {
-         var worlds = new World[count];
-
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            for (int i = 0; i < count; i++)
-            {
-               worlds[i] = await ReadSingleRow(dbCommand);
-               dbDataParameter.Value = _random.Next(1, 10001);
-            }
-         }
-
-         return worlds;
-      }
-
-      public static async Task<List<Fortune>> LoadFortunesRows()
-      {
-         var fortunes = new List<Fortune>();
-
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var dbCommand = new DbCommand("SELECT * FROM fortune", pooledConnection);
-
-         using (dbCommand)
-         {
-            var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess);
-
-            while (dataReader.Read())
-            {
-               fortunes.Add(new Fortune
-               (
-                   id: dataReader.GetInt32(0),
-#if MYSQL
-               //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET;
-               //as a solution we custom read this string
-                message: ReadColumn(dataReader, 1)
-#else
-                message: dataReader.GetString(1)
-#endif
-            ));
-            }
-
-            dataReader.Close();
-         }
-
-         fortunes.Add(new Fortune(id: 0, message: "Additional fortune added at request time."));
-         fortunes.Sort();
-
-         return fortunes;
-      }
-
-      public static async Task<World[]> LoadMultipleUpdatesRows(int count)
-      {
-         var worlds = new World[count];
-
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (queryCommand)
-         {
-            for (int i = 0; i < count; i++)
-            {
-               worlds[i] = await ReadSingleRow(queryCommand);
-               dbDataParameter.Value = _random.Next(1, 10001);
-            }
-         }
-
-         using var updateCommand = new DbCommand(PlatformBenchmarks.BatchUpdateString.Query(count), pooledConnection);
-
-         var ids = PlatformBenchmarks.BatchUpdateString.Ids;
-         var randoms = PlatformBenchmarks.BatchUpdateString.Randoms;
-
-#if !MYSQL
-         var jds = PlatformBenchmarks.BatchUpdateString.Jds;
-#endif      
-
-         for (int i = 0; i < count; i++)
-         {
-            var randomNumber = _random.Next(1, 10001);
-
-            updateCommand.CreateParameter(ids[i], DbType.Int32, worlds[i].Id);
-            updateCommand.CreateParameter(randoms[i], DbType.Int32, randomNumber);
-
-            worlds[i].RandomNumber = randomNumber;
-         }
-
-#if !MYSQL
-         for (int i = 0; i < count; i++)
-         {
-            updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
-         }
-#endif
-
-         await updateCommand.ExecuteNonQueryAsync();
-
-         return worlds;
-      }
-
-      private static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection)
-      {
-#if ADO         
-         var dbCommand = new DbCommand("SELECT * FROM world WHERE id=@Id", pooledConnection);
-#else         
-         var dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
-#endif         
-
-         return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
-      }
-
-      private static async Task<World> ReadSingleRow(DbCommand dbCommand)
-      {
-         var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow & CommandBehavior.SequentialAccess);
-
-         dataReader.Read();
-
-         var world = new World
-         {
-            Id = dataReader.GetInt32(0),
-            RandomNumber = dataReader.GetInt32(1)
-         };
-
-         dataReader.Close();
-
-         return world;
-      }
-
-      public static async Task<World[]> ReadMultipleRows(int count)
-      {
-         int j = 0;
-         var ids = PlatformBenchmarks.BatchUpdateString.Ids;
-         var worlds = new World[count];
-         string queryString;
-
-         if (_queriesMultipleRows[count] != null)
-         {
-            queryString = _queriesMultipleRows[count];
-         }
-         else
-         {
-            var stringBuilder = PlatformBenchmarks.StringBuilderCache.Acquire();
-
-            for (int i = 0; i < count; i++)
-            {
-               stringBuilder.Append("SELECT * FROM world WHERE id=?;");
-            }
-
-            queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder);
-         }
-
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         using var dbCommand = new DbCommand(queryString, pooledConnection);
-
-         for (int i = 0; i < count; i++)
-         {
-            dbCommand.CreateParameter(ids[i], DbType.Int32, _random.Next(1, 10001));
-         }
-
-         var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.Default & CommandBehavior.SequentialAccess);
-
-         do
-         {
-            dataReader.Read();
-
-            worlds[j] = new World
-            {
-               Id = dataReader.GetInt32(0),
-               RandomNumber = dataReader.GetInt32(1)
-            };
-
-            j++;
-         } while (await dataReader.NextResultAsync());
-
-         dataReader.Close();
-
-         return worlds;
-      }
-
-      public static string ReadColumn(IDataReader dataReader, int column)
-      {
-         long size = dataReader.GetBytes(column, 0, null, 0, 0);  //get the length of data
-         byte[] values = new byte[size];
-
-         int bufferSize = 64;
-         long bytesRead = 0;
-         int currentPosition = 0;
-
-         while (bytesRead < size)
-         {
-            bytesRead += dataReader.GetBytes(column, currentPosition, values, currentPosition, bufferSize);
-            currentPosition += bufferSize;
-         }
-
-         return System.Text.Encoding.Default.GetString(values);
-      }
-
-      public static async Task PopulateCache()
-      {
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            var cacheKeys = _cacheKeys;
-            var cache = _cache;
-
-            for (var i = 1; i < 10001; i++)
-            {
-               dbDataParameter.Value = i;
-               cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(dbCommand));
-            }
-         }
-      }
-
-      public static Task<CachedWorld[]> LoadCachedQueries(int count)
-      {
-         var result = new CachedWorld[count];
-         var cacheKeys = _cacheKeys;
-         var cache = _cache;
-         var random = _random;
-
-         for (var i = 0; i < result.Length; i++)
-         {
-            var id = random.Next(1, 10001);
-            var key = cacheKeys[id];
-
-            if (cache.TryGetValue(key, out object cached))
-            {
-               result[i] = (CachedWorld)cached;
-            }
-            else
-            {
-               //return LoadUncachedQueries(id, i, count, this, result);
-               return LoadUncachedQueries(id, i, count, result);
-            }
-         }
-
-         return Task.FromResult(result);
-      }
-
-      static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, CachedWorld[] result)
-      {
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
-            {
-               return await ReadSingleRow(dbCommand);
-            };
-
-            var cacheKeys = _cacheKeys;
-            var key = cacheKeys[id];
-
-            dbDataParameter.Value = id;
-
-            for (; i < result.Length; i++)
-            {
-               result[i] = await _cache.GetOrCreateAsync<CachedWorld>(key, create);
-
-               id = _random.Next(1, 10001);
-               dbDataParameter.Value = id;
-               key = cacheKeys[id];
-            }
-         }
-
-         return result;
-      }
-   }
-}

+ 10 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs

@@ -0,0 +1,10 @@
+using appMpower.Orm.Data; 
+
+namespace appMpower.Orm
+{
+  public static class Constants
+  {
+    public static Dbms Dbms = Dbms.PostgreSQL; 
+    public static DbProvider DbProvider = DbProvider.ODBC; 
+  }
+}

+ 42 - 63
frameworks/CSharp/appmpower/src/Data/DbCommand.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs

@@ -1,56 +1,46 @@
 using System.Data;
-using System.Threading.Tasks;
+using System.Data.Odbc; 
 
-namespace appMpower.Data
+namespace appMpower.Orm.Data
 {
    public class DbCommand : IDbCommand
    {
-      private IDbCommand _dbCommand;
+      private OdbcCommand _odbcCommand;
       private DbConnection _dbConnection;
 
       public DbCommand(DbConnection dbConnection)
       {
-         _dbCommand = dbConnection.CreateCommand();
+         _odbcCommand = (OdbcCommand)dbConnection.CreateCommand();
          _dbConnection = dbConnection;
       }
 
       public DbCommand(string commandText, DbConnection dbConnection)
       {
-         dbConnection.GetCommand(commandText, CommandType.Text, this);
-      }
-
-      public DbCommand(string commandText, CommandType commandType, DbConnection dbConnection)
-      {
-         dbConnection.GetCommand(commandText, commandType, this);
+         _odbcCommand = dbConnection.GetCommand(commandText, CommandType.Text);
+         _dbConnection = dbConnection;
       }
 
-      internal DbCommand(IDbCommand dbCommand, DbConnection dbConnection)
+      public DbCommand(string commandText, DbConnection dbConnection, bool keyed)
       {
-         _dbCommand = dbCommand;
+         _odbcCommand = dbConnection.GetCommand(commandText, CommandType.Text, keyed);
          _dbConnection = dbConnection;
       }
 
-      internal IDbCommand Command
+      public DbCommand(string commandText, CommandType commandType, DbConnection dbConnection)
       {
-         get
-         {
-            return _dbCommand;
-         }
-         set
-         {
-            _dbCommand = value;
-         }
+         _odbcCommand = dbConnection.GetCommand(commandText, commandType);
+         _dbConnection = dbConnection; 
       }
 
-      internal DbConnection DbConnection
+      internal IDbCommand Command
       {
          get
          {
-            return _dbConnection;
+            return _odbcCommand;
          }
          set
          {
-            _dbConnection = value;
+            _odbcCommand = (OdbcCommand)value;
          }
       }
 
@@ -58,11 +48,11 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.CommandText;
+            return _odbcCommand.CommandText;
          }
          set
          {
-            _dbCommand.CommandText = value;
+            _odbcCommand.CommandText = value;
          }
       }
 
@@ -70,22 +60,22 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.CommandTimeout;
+            return _odbcCommand.CommandTimeout;
          }
          set
          {
-            _dbCommand.CommandTimeout = value;
+            _odbcCommand.CommandTimeout = value;
          }
       }
       public CommandType CommandType
       {
          get
          {
-            return _dbCommand.CommandType;
+            return _odbcCommand.CommandType;
          }
          set
          {
-            _dbCommand.CommandType = value;
+            _odbcCommand.CommandType = value;
          }
       }
 
@@ -94,11 +84,11 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.Connection;
+            return _odbcCommand.Connection;
          }
          set
          {
-            _dbCommand.Connection = (IDbConnection?)value;
+            _odbcCommand.Connection = (OdbcConnection?)value;
          }
       }
 #nullable disable
@@ -107,7 +97,7 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.Parameters;
+            return _odbcCommand.Parameters;
          }
       }
 
@@ -116,11 +106,11 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.Transaction;
+            return _odbcCommand.Transaction;
          }
          set
          {
-            _dbCommand.Transaction = (IDbTransaction?)value;
+            _odbcCommand.Transaction = (OdbcTransaction?)value;
          }
       }
 #nullable disable
@@ -129,21 +119,21 @@ namespace appMpower.Data
       {
          get
          {
-            return _dbCommand.UpdatedRowSource;
+            return _odbcCommand.UpdatedRowSource;
          }
          set
          {
-            _dbCommand.UpdatedRowSource = value;
+            _odbcCommand.UpdatedRowSource = value;
          }
       }
       public void Cancel()
       {
-         _dbCommand.Cancel();
+         _odbcCommand.Cancel();
       }
 
       public IDbDataParameter CreateParameter()
       {
-         return _dbCommand.CreateParameter();
+         return _odbcCommand.CreateParameter();
       }
 
       public IDbDataParameter CreateParameter(string name, object value)
@@ -153,21 +143,21 @@ namespace appMpower.Data
 
       public IDbDataParameter CreateParameter(string name, DbType dbType, object value)
       {
-         IDbDataParameter dbDataParameter = null;
+         IDbDataParameter dbDataParameter;
 
-         if (this.Parameters.Contains(name))
+         if (_odbcCommand.Parameters.Contains(name))
          {
-            dbDataParameter = this.Parameters[name] as IDbDataParameter;
+            dbDataParameter = _odbcCommand.Parameters[name];
             dbDataParameter.Value = value;
          }
          else
          {
-            dbDataParameter = _dbCommand.CreateParameter();
+            dbDataParameter = _odbcCommand.CreateParameter();
 
             dbDataParameter.ParameterName = name;
             dbDataParameter.DbType = dbType;
             dbDataParameter.Value = value;
-            this.Parameters.Add(dbDataParameter);
+            _odbcCommand.Parameters.Add(dbDataParameter);
          }
 
          return dbDataParameter;
@@ -175,51 +165,40 @@ namespace appMpower.Data
 
       public int ExecuteNonQuery()
       {
-         return _dbCommand.ExecuteNonQuery();
+         return _odbcCommand.ExecuteNonQuery();
       }
 
       public IDataReader ExecuteReader()
       {
-         return _dbCommand.ExecuteReader();
+         return _odbcCommand.ExecuteReader();
       }
 
       public async Task<int> ExecuteNonQueryAsync()
       {
-         return await (_dbCommand as System.Data.Common.DbCommand).ExecuteNonQueryAsync();
-      }
-
-      public async Task<System.Data.Common.DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
-      {
-         return await (_dbCommand as System.Data.Common.DbCommand).ExecuteReaderAsync(behavior);
+         return await (_odbcCommand as System.Data.Common.DbCommand).ExecuteNonQueryAsync();
       }
 
       public IDataReader ExecuteReader(CommandBehavior behavior)
       {
-         return _dbCommand.ExecuteReader(behavior);
+         return _odbcCommand.ExecuteReader(behavior);
       }
 
 #nullable enable
       public object? ExecuteScalar()
       {
-         return _dbCommand.ExecuteScalar();
-      }
-#nullable disable
-
-#nullable enable
-      public async Task<object?> ExecuteScalarAsync()
-      {
-         return await ((System.Data.Common.DbCommand)_dbCommand).ExecuteScalarAsync();
+         return _odbcCommand.ExecuteScalar();
       }
 #nullable disable
 
       public void Prepare()
       {
-         _dbCommand.Prepare();
+         _odbcCommand.Prepare();
       }
 
       public void Dispose()
       {
-         _dbConnection.ReleaseCommand(this);
+         if (_dbConnection._keyed) _dbConnection._keyedOdbcCommands.TryAdd(_odbcCommand.CommandText, _odbcCommand);
+         else _dbConnection._odbcCommands.Push(_odbcCommand);
       }
    }
 }

+ 153 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs

@@ -0,0 +1,153 @@
+using System.Collections.Concurrent;
+using System.Data;
+using System.Data.Odbc; 
+
+namespace appMpower.Orm.Data
+{
+   public class DbConnection : IDbConnection
+   {
+      private string _connectionString;
+      internal bool _keyed = false; 
+      internal int _number; 
+      internal OdbcConnection _odbcConnection;
+      internal ConcurrentStack<OdbcCommand> _odbcCommands = new();
+      internal Dictionary<string, OdbcCommand> _keyedOdbcCommands;
+
+      public DbConnection()
+      {
+         _connectionString = DbProviderFactory.ConnectionString;
+      }
+
+      public DbConnection(string connectionString)
+      {
+         _connectionString = connectionString;
+      }
+
+      public IDbConnection Connection
+      {
+         get
+         {
+            return _odbcConnection;
+         }
+         set
+         {
+            _odbcConnection = (OdbcConnection)value;
+         }
+      }
+
+      public string ConnectionString
+      {
+         get
+         {
+            return _odbcConnection.ConnectionString;
+         }
+         set
+         {
+            _odbcConnection.ConnectionString = value;
+         }
+      }
+
+      public int ConnectionTimeout
+      {
+         get
+         {
+            return _odbcConnection.ConnectionTimeout;
+         }
+      }
+
+      public string Database
+      {
+         get
+         {
+            return _odbcConnection.Database;
+         }
+      }
+
+      public ConnectionState State
+      {
+         get
+         {
+            if (_odbcConnection is null) return ConnectionState.Closed;
+            return _odbcConnection.State;
+         }
+      }
+
+      public IDbTransaction BeginTransaction()
+      {
+         return _odbcConnection.BeginTransaction();
+      }
+
+      public IDbTransaction BeginTransaction(IsolationLevel il)
+      {
+         return _odbcConnection.BeginTransaction(il);
+      }
+
+      public void ChangeDatabase(string databaseName)
+      {
+         _odbcConnection.ChangeDatabase(databaseName);
+      }
+
+      public void Close()
+      {
+         _odbcConnection.Close();
+      }
+
+      public IDbCommand CreateCommand()
+      {
+         return _odbcConnection.CreateCommand();
+      }
+
+      public void Open()
+      {
+         if (_odbcConnection is null)
+         {
+            DbConnections.GetConnection(_connectionString, this);
+         }
+
+         if (_odbcConnection.State == ConnectionState.Closed)
+         {
+            _odbcConnection.Open();
+         }
+      }
+
+      public void Dispose()
+      {
+         DbConnections.Release(this);
+      }
+
+      internal OdbcCommand GetCommand(string commandText, CommandType commandType, bool keyed = false)
+      {
+         OdbcCommand odbcCommand;
+
+         if (_odbcCommands.TryPop(out odbcCommand))
+         {
+            if (commandText != odbcCommand.CommandText)
+            {
+               odbcCommand.CommandText = commandText; 
+               odbcCommand.Parameters.Clear();
+            }
+
+            return odbcCommand; 
+         }
+         else if (_keyed && _keyedOdbcCommands.TryGetValue(commandText, out odbcCommand))
+         {
+            return odbcCommand; 
+         }
+         else
+         {
+            if (!_keyed && keyed) 
+            {
+               _keyedOdbcCommands = new();
+               _keyed = keyed; 
+            }
+            
+            odbcCommand = _odbcConnection.CreateCommand();
+            odbcCommand.CommandText = commandText;
+            odbcCommand.CommandType = commandType;
+            odbcCommand.Prepare();
+
+            return odbcCommand;
+         }
+      }
+   }
+}

+ 61 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs

@@ -0,0 +1,61 @@
+using System.Collections.Concurrent;
+
+namespace appMpower.Orm.Data
+{
+   public static class DbConnections
+   {
+      private static short _createdConnections = 0;
+      private static ConcurrentStack<DbConnection> _connectionsStack = new();
+
+      public static DbConnection GetConnection(string connectionString)
+      {
+         DbConnection popDbConnection; 
+
+         if (!_connectionsStack.TryPop(out popDbConnection))
+         {
+            popDbConnection = new DbConnection();
+            popDbConnection._odbcConnection = new System.Data.Odbc.OdbcConnection(connectionString);
+
+            _createdConnections++;
+            popDbConnection._number = _createdConnections;
+
+            if (_createdConnections % 25 == 0)
+            {
+               Console.WriteLine("Pooled connections created: " + _createdConnections.ToString());
+            }
+         }
+
+         return popDbConnection; 
+      }
+
+
+      public static void GetConnection(string connectionString, DbConnection dbConnection)
+      {
+         DbConnection popDbConnection = null;
+
+         if (_connectionsStack.TryPop(out popDbConnection))
+         {
+            dbConnection._odbcConnection = popDbConnection._odbcConnection; 
+            dbConnection._odbcCommands = popDbConnection._odbcCommands;
+            dbConnection._number = popDbConnection._number; 
+         }
+         else
+         {
+            dbConnection._odbcConnection = new System.Data.Odbc.OdbcConnection(connectionString);
+
+            _createdConnections++;
+            dbConnection._number = _createdConnections;
+
+           if (_createdConnections % 25 == 0)
+            {
+               Console.WriteLine("Pooled connections created: " + _createdConnections.ToString());
+            }
+          }
+      }
+
+      public static void Release(DbConnection dbConnection)
+      {
+         _connectionsStack.Push(dbConnection);
+      }
+   }
+}

+ 8 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs

@@ -0,0 +1,8 @@
+namespace appMpower.Orm.Data
+{
+   public enum DbProvider
+   {
+      ADO = 0,
+      ODBC = 1,
+   }
+}

+ 21 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs

@@ -0,0 +1,21 @@
+using System.Data;
+
+namespace appMpower.Orm.Data
+{
+   public static class DbProviderFactory
+   {
+      public static string ConnectionString; 
+
+      public static void SetConnectionString()
+      {
+         if (Constants.Dbms == Dbms.MySQL)
+         {
+            ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1;sslmode=DISABLED;CharSet=utf8;"; 
+         }
+         else
+         {
+            ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false;sslmode=disable";
+         }
+      }
+   }
+}

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs

@@ -0,0 +1,9 @@
+namespace appMpower.Orm.Data
+{
+   public enum Dbms
+   {
+      MySQL = 0,
+      PostgreSQL = 1,
+      SQLServer = 2,
+   }
+}

+ 66 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs

@@ -0,0 +1,66 @@
+using System.Runtime.InteropServices;
+using System.Text; 
+using System.Text.Json; 
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects; 
+using appMpower.Orm.Serializers; 
+
+namespace appMpower.Orm;
+
+//These methods are for test purposes only; not used in actual execution
+public static class DotnetMethods
+{
+    private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
+    private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer();
+
+    public static byte[] Db()
+    {
+        var world = RawDb.LoadSingleQueryRow();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Query(int queries)
+    {
+        World[] worlds = RawDb.ReadMultipleRows(queries);
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Updates(int count)
+    {
+        World[] worlds = RawDb.LoadMultipleUpdatesRows(count);
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Fortunes()
+    {
+        List<Fortune> fortunes = RawDb.LoadFortunesRows(); 
+        string fortunesView = FortunesView.Render(fortunes);
+        byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView);
+
+        return byteArray.ToArray();
+    }
+}

+ 4 - 17
frameworks/CSharp/appmpower/src/FortunesView.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs

@@ -1,33 +1,23 @@
 using System.Collections.Generic;
-using System.IO.Pipelines;
 using System.Globalization;
 using System.Text;
 using System.Threading.Tasks;
 using System.Web;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
 using PlatformBenchmarks;
+using appMpower.Orm.Objects;
 
-namespace appMpower
+namespace appMpower.Orm
 {
    public static class FortunesView
    {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", "k");
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", "text/html; charset=UTF-8");
-
       public static char[] _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>".ToCharArray();
       public static char[] _fortunesRowStart = "<tr><td>".ToCharArray();
       public static char[] _fortunesColumn = "</td><td>".ToCharArray();
       public static char[] _fortunesRowEnd = "</td></tr>".ToCharArray();
       public static char[] _fortunesTableEnd = "</table></body></html>".ToCharArray();
 
-      public static async Task Render(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, List<Fortune> fortunes)
+      public static string Render(List<Fortune> fortunes)
       {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
          var writer = StringBuilderCache.Acquire();
 
          writer.Append(_fortunesTableStart);
@@ -39,10 +29,7 @@ namespace appMpower
 
          writer.Append(_fortunesTableEnd);
 
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", (writer.Length + 32).ToString()));
-
-         await pipeWriter.WriteAsync(Encoding.UTF8.GetBytes(StringBuilderCache.GetStringAndRelease(writer)));
-         pipeWriter.Complete();
+         return StringBuilderCache.GetStringAndRelease(writer);
       }
    }
 }

+ 19 - 37
frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs

@@ -1,7 +1,8 @@
 // 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.Linq;
+using appMpower.Orm; 
+using appMpower.Orm.Data; 
 
 namespace PlatformBenchmarks
 {
@@ -33,51 +34,32 @@ namespace PlatformBenchmarks
          //sb.Append("(?::int,?::int)) AS temp(id, randomNumber) WHERE temp.id = world.id");
          */
 
-#if MYSQL
-         for (int i = 0; i < batchSize; i++)
+         if (Constants.Dbms == Dbms.MySQL)
          {
-            sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
+            for (int i = 0; i < batchSize; i++)
+            {
+               sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
+            }
          }
-#elif ADO
-         /*
-         sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
-         Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@i{i}, @r{i}), "));
-         sb.Append($"(@i{lastIndex}, @r{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
-         */
-
-         sb.Append("UPDATE world SET randomNumber=CASE id ");
-
-         for (int i = 0; i < batchSize; i++)
-         {
-            sb.Append("WHEN @i" + i + " THEN @r" + i + " ");
-         }
-
-         sb.Append("ELSE randomnumber END WHERE id IN(");
-
-         for (int i = 0; i < lastIndex; i++)
+         else
          {
-            sb.Append("@j" + i + ",");
-         }
+            sb.Append("UPDATE world SET randomNumber=CASE id ");
 
-         sb.Append("@j" + lastIndex + ")");
-#else
-         sb.Append("UPDATE world SET randomNumber=CASE id ");
+            for (int i = 0; i < batchSize; i++)
+            {
+               sb.Append("WHEN ? THEN ? ");
+            }
 
-         for (int i = 0; i < batchSize; i++)
-         {
-            sb.Append("WHEN ? THEN ? ");
-         }
+            sb.Append("ELSE randomnumber END WHERE id IN(");
 
-         sb.Append("ELSE randomnumber END WHERE id IN(");
+            for (int i = 0; i < lastIndex; i++)
+            {
+               sb.Append("?,");
+            }
 
-         for (int i = 0; i < lastIndex; i++)
-         {
-            sb.Append("?,");
+            sb.Append("?)");
          }
 
-         sb.Append("?)");
-#endif
-
          return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
       }
    }

+ 0 - 2
frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs

@@ -1,9 +1,7 @@
 // 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
 {

+ 54 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs

@@ -0,0 +1,54 @@
+// 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.Text;
+
+namespace PlatformBenchmarks; 
+
+internal static class StringBuilderCache
+{
+    private const int DefaultCapacity = 1386;
+    private const int MaxBuilderSize = DefaultCapacity * 3;
+    [ThreadStatic]
+    private static StringBuilder t_cachedInstance;
+
+    public static StringBuilder Acquire(int capacity = DefaultCapacity)
+    {
+        if (capacity <= MaxBuilderSize)
+        {
+            StringBuilder sb = t_cachedInstance;
+
+            if (capacity < DefaultCapacity)
+            {
+                capacity = DefaultCapacity;
+            }
+
+            if (sb != null)
+            {
+                if (capacity <= sb.Capacity)
+                {
+                    t_cachedInstance = null;
+                    sb.Clear();
+                    return sb;
+                }
+            }
+        }
+
+        return new StringBuilder(capacity);
+    }
+
+    public static void Release(StringBuilder sb)
+    {
+        if (sb.Capacity <= MaxBuilderSize)
+        {
+            t_cachedInstance = sb;
+        }
+    }
+
+    public static string GetStringAndRelease(StringBuilder sb)
+    {
+        string result = sb.ToString();
+        Release(sb);
+        return result;
+    }
+}

+ 144 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs

@@ -0,0 +1,144 @@
+using System.Runtime.InteropServices;
+using System.Text; 
+using System.Text.Json; 
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects; 
+using appMpower.Orm.Serializers; 
+
+namespace appMpower.Orm;
+
+public static class NativeMethods
+{
+    private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
+    private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer();
+
+    [UnmanagedCallersOnly(EntryPoint = "Dbms")]
+    public static void Dbms(int dbms)
+    {
+        Constants.Dbms = (Dbms)dbms; 
+        DbProviderFactory.SetConnectionString();
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "DbProvider")]
+    public static void DbProvider(int dbProvider)
+    {
+        Constants.DbProvider = (DbProvider)dbProvider; 
+        DbProviderFactory.SetConnectionString();
+    }
+    
+    [UnmanagedCallersOnly(EntryPoint = "FreeHandlePointer")]
+    public static void FreeHandlePointer(IntPtr handlePointer)
+    {
+        GCHandle handle = GCHandle.FromIntPtr(handlePointer);
+        handle.Free();
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Db")]
+    public static unsafe IntPtr Db(int* length, IntPtr* handlePointer)
+    {
+        var world = RawDb.LoadSingleQueryRow();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        // return the managed and byteArrayPointer pointer
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+        /*
+        fixed(byte* b = memoryStream.ToArray())
+        {
+            return b; 
+        }
+        */
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Fortunes")]
+    public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
+    {
+        List<Fortune> fortunes = RawDb.LoadFortunesRows(); 
+        string fortunesView = FortunesView.Render(fortunes);
+        byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView);
+
+        *length = byteArray.Length; 
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Query")]
+    public static unsafe IntPtr Query(int queries, int* length, IntPtr* handlePointer)
+    {
+        World[] worlds = RawDb.ReadMultipleRows(queries);
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Updates")]
+    public static unsafe IntPtr Updates(int count, int* length, IntPtr* handlePointer)
+    {
+        World[] worlds = RawDb.LoadMultipleUpdatesRows(count);
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "DbById")]
+    public static unsafe IntPtr DbById(int id, int* length, IntPtr* handlePointer)
+    {
+        var world = RawDb.LoadSingleQueryRowById(id);
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+}

+ 1 - 1
frameworks/CSharp/appmpower/src/CachedWorld.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs

@@ -1,4 +1,4 @@
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
    public struct CachedWorld
    {

+ 1 - 3
frameworks/CSharp/appmpower/src/Fortune.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs

@@ -1,6 +1,4 @@
-using System;
-
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
    public readonly struct Fortune : IComparable<Fortune>, IComparable
    {

+ 1 - 1
frameworks/CSharp/appmpower/src/World.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs

@@ -1,4 +1,4 @@
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
    public struct World
    {

+ 257 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs

@@ -0,0 +1,257 @@
+using System.Data;
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects;
+using PlatformBenchmarks;
+
+namespace appMpower.Orm
+{
+   public static class RawDb
+   {
+      private const int MaxBatch = 500;
+
+      private static Random _random = new Random();
+
+      private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
+
+      public static World LoadSingleQueryRow()
+      {
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         var (dbCommand, _) = CreateReadCommand(pooledConnection);
+
+         using (dbCommand)
+         {
+            World world = ReadSingleRow(dbCommand);
+
+            return world;
+         }
+      }
+
+      public static World LoadSingleQueryRowById(int id)
+      {
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         var (dbCommand, _) = CreateReadCommandById(pooledConnection, id);
+
+         using (dbCommand)
+         {
+            World world = ReadSingleRow(dbCommand);
+
+            return world;
+         }
+      }
+
+      public static World[] LoadMultipleQueriesRows(int count)
+      {
+         var worlds = new World[count];
+
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
+
+         using (dbCommand)
+         {
+            for (int i = 0; i < count; i++)
+            {
+               worlds[i] = ReadSingleRow(dbCommand);
+               dbDataParameter.Value = _random.Next(1, 10001);
+            }
+         }
+
+         return worlds;
+      }
+
+      public static List<Fortune> LoadFortunesRows()
+      {
+         var fortunes = new List<Fortune>();
+
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         var dbCommand = new DbCommand("SELECT * FROM fortune", pooledConnection);
+
+         using (dbCommand)
+         {
+            IDataReader dataReader = dbCommand.ExecuteReader(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess);
+
+            while (dataReader.Read())
+            {
+               fortunes.Add(new Fortune
+               (
+                  id: dataReader.GetInt32(0),
+                  //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET;
+                  //as a solution we custom read this string
+                  message: (Constants.Dbms == Dbms.MySQL ? ReadColumn(dataReader, 1) : dataReader.GetString(1))
+               ));
+            }
+
+            dataReader.Close();
+         }
+
+         fortunes.Add(new Fortune(id: 0, message: "Additional fortune added at request time."));
+         fortunes.Sort();
+
+         return fortunes;
+      }
+
+      public static World[] LoadMultipleUpdatesRows(int count)
+      {
+         var worlds = new World[count];
+
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection, true);
+
+         using (queryCommand)
+         {
+            for (int i = 0; i < count; i++)
+            {
+               worlds[i] = ReadSingleRow(queryCommand);
+               dbDataParameter.Value = _random.Next(1, 10001);
+            }
+         }
+
+         using var updateCommand = new DbCommand(BatchUpdateString.Query(count), pooledConnection, true);
+
+         var ids = BatchUpdateString.Ids;
+         var randoms = BatchUpdateString.Randoms;
+
+         for (int i = 0; i < count; i++)
+         {
+            var randomNumber = _random.Next(1, 10001);
+
+            updateCommand.CreateParameter(ids[i], DbType.Int32, worlds[i].Id);
+            updateCommand.CreateParameter(randoms[i], DbType.Int32, randomNumber);
+
+            worlds[i].RandomNumber = randomNumber;
+         }
+
+         if (Constants.Dbms != Dbms.MySQL)
+         {
+            var jds = BatchUpdateString.Jds;
+
+            for (int i = 0; i < count; i++)
+            {
+               updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
+            }
+         }
+
+         updateCommand.ExecuteNonQuery();
+
+         return worlds;
+      }
+
+      internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection)
+      {
+         DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
+
+         return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
+      }
+
+      internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection, bool keyed)
+      {
+         DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection, keyed);
+
+         return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
+      }
+
+      internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommandById(DbConnection pooledConnection, int id)
+      {
+         DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
+
+         return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, id));
+      }
+
+      internal static World ReadSingleRow(DbCommand dbCommand)
+      {
+         var dataReader = dbCommand.ExecuteReader(CommandBehavior.SingleRow & CommandBehavior.SequentialAccess);
+
+         dataReader.Read();
+
+         var world = new World
+         {
+            Id = dataReader.GetInt32(0),
+            RandomNumber = dataReader.GetInt32(1)
+         };
+
+         dataReader.Close();
+
+         return world;
+      }
+
+      public static World[] ReadMultipleRows(int count)
+      {
+         int j = 0;
+         var ids = BatchUpdateString.Ids;
+         var worlds = new World[count];
+         string queryString;
+
+         if (_queriesMultipleRows[count] != null)
+         {
+            queryString = _queriesMultipleRows[count];
+         }
+         else
+         {
+            var stringBuilder = StringBuilderCache.Acquire();
+
+            for (int i = 0; i < count; i++)
+            {
+               stringBuilder.Append("SELECT * FROM world WHERE id=?;");
+            }
+
+            queryString = _queriesMultipleRows[count] = StringBuilderCache.GetStringAndRelease(stringBuilder);
+         }
+
+         using var pooledConnection = DbConnections.GetConnection(DbProviderFactory.ConnectionString);
+         pooledConnection.Open();
+
+         using var dbCommand = new DbCommand(queryString, pooledConnection);
+
+         for (int i = 0; i < count; i++)
+         {
+            dbCommand.CreateParameter(ids[i], DbType.Int32, _random.Next(1, 10001));
+         }
+
+         var dataReader = dbCommand.ExecuteReader(CommandBehavior.Default & CommandBehavior.SequentialAccess);
+
+         do
+         {
+            dataReader.Read();
+
+            worlds[j] = new World
+            {
+               Id = dataReader.GetInt32(0),
+               RandomNumber = dataReader.GetInt32(1)
+            };
+
+            j++;
+         } while (dataReader.NextResult());
+
+         dataReader.Close();
+
+         return worlds;
+      }
+
+      public static string ReadColumn(IDataReader dataReader, int column)
+      {
+         long size = dataReader.GetBytes(column, 0, null, 0, 0);  //get the length of data
+         byte[] values = new byte[size];
+
+         int bufferSize = 64;
+         long bytesRead = 0;
+         int currentPosition = 0;
+
+         while (bytesRead < size)
+         {
+            bytesRead += dataReader.GetBytes(column, currentPosition, values, currentPosition, bufferSize);
+            currentPosition += bufferSize;
+         }
+
+         return System.Text.Encoding.Default.GetString(values);
+      }
+   }
+}

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs

@@ -0,0 +1,9 @@
+using System.Text.Json;
+
+namespace appMpower.Orm.Serializers
+{
+   public interface IJsonSerializer<T>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, T t);
+   }
+}

+ 4 - 2
frameworks/CSharp/appmpower/src/WorldSerializer.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs

@@ -1,8 +1,9 @@
 using System.Text.Json;
+using appMpower.Orm.Objects;
 
-namespace appMpower
+namespace appMpower.Orm.Serializers
 {
-   public class WorldSerializer : Kestrel.IJsonSerializer<World>
+   public class WorldSerializer : IJsonSerializer<World>
    {
       public void Serialize(Utf8JsonWriter utf8JsonWriter, World world)
       {
@@ -10,6 +11,7 @@ namespace appMpower
          utf8JsonWriter.WriteNumber("id", world.Id);
          utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
          utf8JsonWriter.WriteEndObject();
+         utf8JsonWriter.Flush();
       }
    }
 }

+ 24 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs

@@ -0,0 +1,24 @@
+using System.Text.Json;
+using appMpower.Orm.Objects;
+
+namespace appMpower.Orm.Serializers
+{
+   public class WorldsSerializer : IJsonSerializer<World[]>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, World[] worlds)
+      {
+         utf8JsonWriter.WriteStartArray();
+
+         foreach (World world in worlds)
+         {
+            utf8JsonWriter.WriteStartObject();
+            utf8JsonWriter.WriteNumber("id", world.Id);
+            utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
+            utf8JsonWriter.WriteEndObject();
+         }
+
+         utf8JsonWriter.WriteEndArray();
+         utf8JsonWriter.Flush();
+      }
+   }
+}

+ 42 - 55
frameworks/CSharp/appmpower/src/appMpower.csproj → frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj

@@ -1,55 +1,42 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
-  <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
-    <OutputType>Exe</OutputType>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-
-    <!--<RuntimeIdentifier>linux-x64</RuntimeIdentifier>-->
-
-    <!-- Normal .NET 8 -->
-    <PublishAot>true</PublishAot>
-    <SelfContained>true</SelfContained>
-    <InvariantGlobalization>true</InvariantGlobalization>
-    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
-    <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
-    <DebugType>none</DebugType>
-    <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
-
-    <!-- Only some may work - From the experimental AOT version -->
-    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
-    <IlcTrimMetadata>true</IlcTrimMetadata>
-    <IlcInvariantGlobalization>true</IlcInvariantGlobalization>
-    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
-
-    <!-- Still works from the experimental AOT version, but high risk -->
-    <IlcDisableReflection>true</IlcDisableReflection>
-
-    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
-
-    <!--
-    <TrimMode>link</TrimMode>
-    -->
-
-    <!-- Opt out of the "easy mode" of the CoreRT compiler (http://aka.ms/OptimizeCoreRT) -->
-    <IlcPgoOptimize>true</IlcPgoOptimize>
-
-    <!-- This benchmark is marked Stripped, so we might as well do this: -->
-    <UseSystemResourceKeys>true</UseSystemResourceKeys>
-    <EventSourceSupport>false</EventSourceSupport>
-    <DebuggerSupport>false</DebuggerSupport>
-
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="System.Data.Odbc" Version="8.0.0" />
-    <PackageReference Include="Npgsql" Version="8.0.2-*" />
-  </ItemGroup>
-
-  <PropertyGroup>
-      <DefineConstants>$(DefineConstants);POSTGRESQL</DefineConstants>
-      <DefineConstants Condition=" '$(Driver)' == 'odbc' ">$(DefineConstants);ODBC</DefineConstants>
-      <DefineConstants Condition=" '$(Driver)' == 'ado' ">$(DefineConstants);ADO</DefineConstants>
-  </PropertyGroup>
-
-</Project>
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+
+    <PublishAot>true</PublishAot>
+    <SelfContained>true</SelfContained>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier> <!-- docker server -->
+    <!--TODOLOCAL-->
+    <!--<RuntimeIdentifier>linux-arm64</RuntimeIdentifier>--> <!-- docker local -->
+    <!--<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>--> <!-- outside docker local -->
+
+    <InvariantGlobalization>true</InvariantGlobalization>
+    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
+    <IlcOptimizationPreference>Size</IlcOptimizationPreference>
+    <DebugType>none</DebugType>
+    <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
+
+    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
+    <IlcTrimMetadata>true</IlcTrimMetadata>
+    <IlcInvariantGlobalization>true</IlcInvariantGlobalization>
+    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
+
+    <IlcDisableReflection>true</IlcDisableReflection>
+
+    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
+
+    <IlcPgoOptimize>true</IlcPgoOptimize>
+
+    <UseSystemResourceKeys>true</UseSystemResourceKeys>
+    <EventSourceSupport>false</EventSourceSupport>
+    <DebuggerSupport>false</DebuggerSupport>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.Data.Odbc" Version="8.0.0" />
+  </ItemGroup>
+
+</Project>

+ 7 - 0
frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs

@@ -0,0 +1,7 @@
+namespace appMpower
+{
+  public struct JsonMessage
+  {
+      public string Message { get; set; }
+  }
+}

+ 120 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs

@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower;
+
+public class CachingMiddleware
+{
+   private static readonly Dictionary<int, byte[]> _cache = new();
+   private static readonly Random _random = new();
+
+   private static readonly byte[] _startBytes = Encoding.UTF8.GetBytes("[");
+   private static readonly byte[] _endBytes = Encoding.UTF8.GetBytes("]");
+   private static readonly byte[] _comma = Encoding.UTF8.GetBytes(",");
+
+
+   private readonly static KeyValuePair<string, StringValues> _headerServer =
+       new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+   private readonly static KeyValuePair<string, StringValues> _headerContentType =
+        new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public CachingMiddleware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+   public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/cached-worlds", StringComparison.Ordinal))
+      {
+         int payloadLength;
+         IntPtr handlePointer;
+         IntPtr bytePointer;
+         byte[] json;
+
+         if (_cache.Count == 0)
+         {
+            for (int i = 1; i < 10001; i++)
+            {
+               bytePointer = NativeMethods.DbById(i, out payloadLength, out handlePointer);
+               json = new byte[payloadLength];
+               Marshal.Copy(bytePointer, json, 0, payloadLength);
+               NativeMethods.FreeHandlePointer(handlePointer);
+               _cache.Add(i, json);
+            }
+         }
+
+         var queryString = httpContext.Request.QueryString.ToString();
+         int queries;
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries);
+         queries = queries > 500 ? 500 : (queries > 0 ? queries : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int queriesLength = 0;
+         int[] keys = new int[queries];
+
+         for (int i = 0; i < queries; i++)
+         {
+            keys[i] = _random.Next(1, 10001);
+            
+            if (!_cache.TryGetValue(keys[i], out json))
+            {
+               bytePointer = NativeMethods.DbById(keys[i], out payloadLength, out handlePointer);
+               json = new byte[payloadLength];
+               Marshal.Copy(bytePointer, json, 0, payloadLength);
+               NativeMethods.FreeHandlePointer(handlePointer);
+               _cache.Add(keys[i], json);
+            }
+
+            queriesLength += json.Length;
+         }
+
+         byte[] result = new byte[_startBytes.Length + _endBytes.Length + (_comma.Length * queries - 1) + queriesLength];
+         int position = 0;
+
+         Buffer.BlockCopy(_startBytes, 0, result, position, _startBytes.Length);
+         position += _startBytes.Length;
+
+         for (int i = 0; i < queries; i++)
+         {
+            json = _cache[keys[i]]; 
+            Buffer.BlockCopy(json, 0, result, position, json.Length);
+            position += json.Length;
+
+            if (i < queries - 1)
+            {
+               Buffer.BlockCopy(_comma, 0, result, position, _comma.Length);
+               position += _comma.Length;
+            }
+         }
+
+         Buffer.BlockCopy(_endBytes, 0, result, position, _endBytes.Length);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", result.Length.ToString()));
+
+         return response.Body.WriteAsync(result, 0, result.Length);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class CachingMiddlewareExtensions
+{
+   public static IApplicationBuilder UseCaching(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<CachingMiddleware>();
+   }
+}

+ 58 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class FortunesMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/html; charset=UTF-8"));
+
+    private readonly RequestDelegate _next;
+
+    public FortunesMiddleware(RequestDelegate next)
+    {
+        _next = next;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal))
+        {
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            int payloadLength;
+            IntPtr handlePointer; 
+
+            IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer);
+            byte[] json = new byte[payloadLength];
+            Marshal.Copy(bytePointer, json, 0, payloadLength);
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(json, 0, payloadLength);
+        }
+
+        return _next(httpContext);
+    }
+}
+
+public static class FortunesMiddlewareExtensions
+{
+    public static IApplicationBuilder UseFortunes(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<FortunesMiddleware>();
+    }
+}

+ 64 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json; 
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+using appMpower.Serializers; 
+
+namespace appMpower; 
+
+public class JsonMiddleware
+{
+    private readonly static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
+
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+         
+    private readonly RequestDelegate _nextStage;
+
+    public JsonMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/json", StringComparison.Ordinal))
+        {
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            using var utf8JsonWriter = new Utf8JsonWriter(httpContext.Response.Body, _jsonWriterOptions);
+
+            _jsonMessageSerializer.Serialize(utf8JsonWriter, new JsonMessage { Message = "Hello, World!" });
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesPending.ToString()));
+
+            utf8JsonWriter.Flush();
+
+            return Task.CompletedTask;
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class JsonMiddlewareExtensions
+{
+    public static IApplicationBuilder UseJson(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<JsonMiddleware>();
+    }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class MultipleQueriesMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public MultipleQueriesMiddleware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/queries", StringComparison.Ordinal))
+      {
+         var queryString = httpContext.Request.QueryString.ToString(); 
+         int queries; 
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries); 
+         queries = queries > 500 ? 500 : (queries > 0 ? queries : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int payloadLength;
+         IntPtr handlePointer;
+
+         IntPtr bytePointer = NativeMethods.Query(queries, out payloadLength, out handlePointer);
+         byte[] json = new byte[payloadLength];
+         Marshal.Copy(bytePointer, json, 0, payloadLength);
+         NativeMethods.FreeHandlePointer(handlePointer);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+         return response.Body.WriteAsync(json, 0, payloadLength);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class MultipleQueriesMiddlewareExtensions
+{
+   public static IApplicationBuilder UseMultipleQueries(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<MultipleQueriesMiddleware>();
+   }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class MultipleUpdatesMiddelware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public MultipleUpdatesMiddelware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/updates", StringComparison.Ordinal))
+      {
+         var queryString = httpContext.Request.QueryString.ToString(); 
+         int count; 
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out count); 
+         count = count > 500 ? 500 : (count > 0 ? count : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int payloadLength;
+         IntPtr handlePointer;
+
+         IntPtr bytePointer = NativeMethods.Updates(count, out payloadLength, out handlePointer);
+         byte[] json = new byte[payloadLength];
+         Marshal.Copy(bytePointer, json, 0, payloadLength);
+         NativeMethods.FreeHandlePointer(handlePointer);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+         return response.Body.WriteAsync(json, 0, payloadLength);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class MultipleUpdatesMiddelwareExtensions
+{
+   public static IApplicationBuilder UseMultipleUpdates(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<MultipleUpdatesMiddelware>();
+   }
+}

+ 50 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public unsafe class PlaintextMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/plain"));
+    private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
+
+    private readonly RequestDelegate _nextStage;
+
+    public PlaintextMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/plaintext", StringComparison.Ordinal))
+        {
+            var payloadLength = _helloWorldPayload.Length;
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class PlaintextMiddlewareExtensions
+{
+    public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<PlaintextMiddleware>();
+    }
+}

+ 57 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs

@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class SingleQueryMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+    private readonly RequestDelegate _nextStage;
+
+    public SingleQueryMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/db", StringComparison.Ordinal))
+        {
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            int payloadLength;
+            IntPtr handlePointer; 
+
+            IntPtr bytePointer = NativeMethods.Db(out payloadLength, out handlePointer);
+            byte[] json = new byte[payloadLength];
+            Marshal.Copy(bytePointer, json, 0, payloadLength);
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(json, 0, payloadLength);
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class SingleQueryMiddlewareExtensions
+{
+    public static IApplicationBuilder UseSingleQuery(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<SingleQueryMiddleware>();
+    }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Runtime.InteropServices;
+
+public unsafe partial class NativeMethods
+{
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern void Dbms(int dbms); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern void DbProvider(int dbProvider); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl)]
+#endif
+    public static extern void FreeHandlePointer(IntPtr handlePointer);
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    //public static extern byte* Db(out int length); 
+    public static extern IntPtr Db(out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Fortunes(out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Query(int queries, out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Updates(int queries, out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr DbById(int id, out int length, out IntPtr handlePointer); 
+}

+ 44 - 0
frameworks/CSharp/appmpower/src/appMpower/Program.cs

@@ -0,0 +1,44 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+
+namespace appMpower; 
+
+class Program
+{
+    static void Main(string[] args)
+    {
+        BuildWebHost(args).Run();
+    }
+
+    static IHost BuildWebHost(string[] args)
+    {
+        var config = new ConfigurationBuilder()
+            .AddJsonFile("appsettings.json")
+            .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+            .AddCommandLine(args)
+            .Build();
+
+        var appSettings = config.GetSection("AppSettings").Get<AppSettings>();
+
+        var host = Host.CreateDefaultBuilder(args)
+            .ConfigureWebHostDefaults(webBuilder =>
+            {
+                webBuilder.UseConfiguration(config)
+                          .UseKestrel(options =>
+                          {
+                            options.AddServerHeader = false; 
+                            options.AllowSynchronousIO = true;
+                          })
+                          .UseStartup<Startup>();
+            })
+            .Build();
+
+        return host;
+    }
+}
+
+public class AppSettings
+{
+    public string Database { get; set; }
+}

+ 1 - 1
frameworks/CSharp/appmpower/src/Kestrel/IJsonSerializer.cs → frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs

@@ -1,6 +1,6 @@
 using System.Text.Json;
 
-namespace appMpower.Kestrel
+namespace appMpower.Serializers
 {
    public interface IJsonSerializer<T>
    {

+ 3 - 3
frameworks/CSharp/appmpower/src/JsonMessageSerializer.cs → frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs

@@ -1,13 +1,13 @@
 using System.Text.Json;
 
-namespace appMpower
+namespace appMpower.Serializers
 {
-   public class JsonMessageSerializer : Kestrel.IJsonSerializer<JsonMessage>
+   public class JsonMessageSerializer : IJsonSerializer<JsonMessage>
    {
       public void Serialize(Utf8JsonWriter utf8JsonWriter, JsonMessage jsonMessage)
       {
          utf8JsonWriter.WriteStartObject();
-         utf8JsonWriter.WriteString("message", jsonMessage.message);
+         utf8JsonWriter.WriteString("message", jsonMessage.Message);
          utf8JsonWriter.WriteEndObject();
       }
    }

+ 56 - 0
frameworks/CSharp/appmpower/src/appMpower/Startup.cs

@@ -0,0 +1,56 @@
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace appMpower; 
+
+public class Startup
+{
+    private readonly IConfiguration _configuration;
+
+    public Startup(IConfiguration configuration)
+    {
+        _configuration = configuration;
+    }
+
+    public void ConfigureServices(IServiceCollection services)
+    {
+        var appSettings = _configuration.Get<AppSettings>();
+        services.AddSingleton(appSettings);
+
+#if !DEBUG
+    #if ODBC      
+        NativeMethods.DbProvider(1); //ODBC
+    #else
+        NativeMethods.DbProvider(0); //ADO
+    #endif        
+
+    #if POSTGRESQL      
+        NativeMethods.Dbms(1); //PostgreSQL
+    #else
+        NativeMethods.Dbms(0); //MySQL
+    #endif
+#endif
+
+        var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+
+        settings.AllowCharacter('—');
+        services.AddWebEncoders(options =>
+        {
+            options.TextEncoderSettings = settings;
+        });
+    }
+
+    public void Configure(IApplicationBuilder app)
+    {
+        app.UsePlainText();
+        app.UseJson();
+        app.UseSingleQuery();
+        app.UseCaching();
+        app.UseFortunes();
+        app.UseMultipleQueries();
+        app.UseMultipleUpdates();
+    }
+}

+ 35 - 0
frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj

@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
+
+    <!--
+    <Content Include="../appMpower.Orm/bin/Release/net8.0/appMpower.Orm.dll">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content> 
+    -->
+    <!--TODOLOCAL-->
+    <!--
+    <Content Include="../appMpower.Orm/bin/Release/net8.0/osx-arm64/native/appMpower.Orm.dylib">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content> 
+    -->
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include = "..\appMpower.Orm\appMpower.Orm.csproj" />
+  </ItemGroup>
+
+  <PropertyGroup>
+      <DefineConstants Condition=" '$(Driver)' == 'odbc' ">$(DefineConstants);ODBC</DefineConstants>
+      <DefineConstants Condition=" '$(Database)' == 'postgresql' ">$(DefineConstants);POSTGRESQL</DefineConstants>
+      <DefineConstants Condition=" '$(Database)' == 'mysql' ">$(DefineConstants);MYSQL</DefineConstants>
+  </PropertyGroup>
+
+</Project>

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower/appsettings.json

@@ -0,0 +1,9 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "None",
+      "Microsoft": "None",
+      "Microsoft.Hosting.Lifetime": "None"
+    }
+  }
+}

+ 0 - 0
frameworks/CSharp/appmpower/src/nuget.config → frameworks/CSharp/appmpower/src/appMpower/nuget.config


+ 28 - 0
frameworks/CSharp/appmpower/src/src.sln

@@ -0,0 +1,28 @@
+
+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}") = "appMpower.Orm", "appMpower.Orm\appMpower.Orm.csproj", "{1EC05247-7299-4F69-887B-3B746DF5F878}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "appMpower", "appMpower\appMpower.csproj", "{EB5D3496-53B9-49A7-A3AD-754078142F81}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1EC05247-7299-4F69-887B-3B746DF5F878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1EC05247-7299-4F69-887B-3B746DF5F878}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1EC05247-7299-4F69-887B-3B746DF5F878}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1EC05247-7299-4F69-887B-3B746DF5F878}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EB5D3496-53B9-49A7-A3AD-754078142F81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EB5D3496-53B9-49A7-A3AD-754078142F81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EB5D3496-53B9-49A7-A3AD-754078142F81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EB5D3496-53B9-49A7-A3AD-754078142F81}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+EndGlobal