Browse Source

azure self test pass

Robert 1 year ago
parent
commit
efef39eaa9

+ 6 - 0
Dockerfile

@@ -6,6 +6,7 @@ EXPOSE 443
 EXPOSE 54088
 WORKDIR /app
 
+
 RUN apt-get update \
     && apt-get install -y --no-install-recommends dialog \
     && apt-get install -y --no-install-recommends openssh-server \
@@ -15,6 +16,11 @@ RUN apt-get update \
 	
 COPY sshd_config /etc/ssh/sshd_config
 
+RUN echo 'net.ipv4.tcp_rmem= 10240 87380 12582912' >> /etc/sysctl.conf
+RUN echo 'net.ipv4.tcp_wmem= 10240 87380 12582912' >> /etc/sysctl.conf
+RUN echo 'net.ipv4.tcp_window_scaling = 1' >> /etc/sysctl.conf
+
+
 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
 WORKDIR /src
 COPY ["EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj", "EVCB_OCPP.WSServer/"]

+ 1 - 1
EVCB_OCPP.WSServer/Helper/AddPortalDbContext.cs

@@ -159,7 +159,7 @@ public class SqlConnectionFactory<T> where T: DbContext
 
         if (t1 > 500)
         {
-            logger.LogWarning($"{nameof(T)} SqlConnection Open slow {t0}/{t1}");
+            logger.LogWarning($"{typeof(T)} SqlConnection Open slow {t0}/{t1}");
         }
 
         return sqlConnection;

+ 8 - 30
EVCB_OCPP.WSServer/Jobs/HeartBeatCheckJob.cs

@@ -1,5 +1,6 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
+using EVCB_OCPP.WSServer.Service;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -19,15 +20,18 @@ public class HeartBeatCheckJob : IJob
     public HeartBeatCheckJob(
         ProtalServer protalServer,
         IDbContextFactory<MainDBContext> maindbContextFactory,
+        IMainDbService mainDbService,
         ILogger<HeartBeatCheckJob> logger)
     {
         this.protalServer = protalServer;
         this.maindbContextFactory = maindbContextFactory;
+        this.mainDbService = mainDbService;
         this.logger = logger;
     }
 
     private readonly ProtalServer protalServer;
     private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+    private readonly IMainDbService mainDbService;
     private readonly ILogger<HeartBeatCheckJob> logger;
 
     public async Task Execute(IJobExecutionContext context)
@@ -43,38 +47,12 @@ public class HeartBeatCheckJob : IJob
 
             watch.Start();
 
-            using (var db = await maindbContextFactory.CreateDbContextAsync())
-            using (var transaction = await db.Database.BeginTransactionAsync())
+            var datas = new List<Machine>();
+            foreach (var session in clients)
             {
-                try
-                {
-                    foreach (var session in clients)
-                    {
-                        var machine = new Machine() { Id = session.MachineId };
-                        if (machine != null)
-                        {
-                            //db.Configuration.AutoDetectChangesEnabled = false;
-                            //db.Configuration.ValidateOnSaveEnabled = false;
-                            db.Machine.Attach(machine);
-                            machine.HeartbeatUpdatedOn = DateTime.UtcNow;
-                            machine.ConnectionType = session.UriScheme.Equals("wss") ? 2 : 1;
-                            db.Entry(machine).Property(x => x.HeartbeatUpdatedOn).IsModified = true;
-                            db.Entry(machine).Property(x => x.ConnectionType).IsModified = true;
-                            //await db.SaveChangesAsync();
-                        }
-                    }
-
-                    await db.SaveChangesAsync();
-                    await transaction.CommitAsync();
-                    db.ChangeTracker.Clear();
-                }
-                catch (Exception ex)
-                {
-                    logger.LogCritical(ex, "HeartBeatCheckTrigger update fail, roll back");
-                    transaction.Rollback();
-                }
+                var machine = new Machine() { Id = session.MachineId, HeartbeatUpdatedOn = cdt, ConnectionType = session.UriScheme.Equals("wss") ? 2 : 1 };
             }
-
+            await mainDbService.UpdateHeartBeats(datas);
 
             watch.Stop();
             if (watch.ElapsedMilliseconds / 1000 > 5)

+ 14 - 7
EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs

@@ -54,14 +54,17 @@ public class ServerMessageJob : IJob
         protalServer.RemoveConfirmMessage();
 
         BasicMessageHandler msgAnalyser = new BasicMessageHandler();
-        using var db = await maindbContextFactory.CreateDbContextAsync();
         var dateTimeNow = DateTime.UtcNow;
         DateTime startDt = dateTimeNow.AddSeconds(-30);
         DateTime dt = new DateTime(1991, 1, 1);
         DateTime currentTime = dateTimeNow;
-        var commandList = await db.ServerMessage.Where(c => c.ReceivedOn == dt && c.UpdatedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= currentTime).AsNoTracking().ToListAsync();
+        List<ServerMessage> commandList;
         var clientDic = protalServer.ClientDic;
 
+        using (var db = await maindbContextFactory.CreateDbContextAsync())
+        {
+            commandList = await db.ServerMessage.Where(c => c.ReceivedOn == dt && c.UpdatedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= currentTime).AsNoTracking().ToListAsync();
+        }
         //處理主機傳送的有指令
         var cmdMachineList = commandList.Select(c => c.ChargeBoxId).Distinct().ToList();
         if (commandList.Count > 0)
@@ -159,12 +162,16 @@ public class ServerMessageJob : IJob
                     //db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
                     //db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
                     // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
-                    db.ServerMessage.Attach(_UpdatedItem);
-                    _UpdatedItem.UpdatedOn = dateTimeNow;
-                    db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
 
-                    await db.SaveChangesAsync();
-                    db.ChangeTracker.Clear();
+                    using (var db = await maindbContextFactory.CreateDbContextAsync())
+                    {
+                        db.ServerMessage.Attach(_UpdatedItem);
+                        _UpdatedItem.UpdatedOn = dateTimeNow;
+                        db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
+
+                        await db.SaveChangesAsync();
+                        db.ChangeTracker.Clear();
+                    }
 
                     #endregion
 

+ 49 - 57
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -11,26 +11,13 @@ using EVCB_OCPP.WSServer.Service;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
-using MongoDB.Driver.Core.Servers;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
-
-using OCPPPackage.Profiles;
 using OCPPServer.Protocol;
-using System;
-using System.Collections.Generic;
-using System.Configuration;
 using System.Data;
 using System.Diagnostics;
 using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-
-using Microsoft.AspNetCore.Http.HttpResults;
-using MongoDB.Driver.Core.Connections;
-using EVCB_OCPP20.Packet.DataTypes;
 using SuperSocket.SocketBase;
 
 namespace EVCB_OCPP.WSServer.Message;
@@ -409,13 +396,14 @@ internal partial class ProfileHandler
                             }
 
                             s2 = meterValueTimer.ElapsedMilliseconds;
-                            List<Task> insertTasks = new();
+                            //List<Task> insertTasks = new();
+                            List <InsertMeterValueParam> datas= new();
                             foreach (var item in _request.meterValue)
                             {
                                 foreach (var sampleVaule in item.sampledValue)
                                 {
                                     decimal value = Convert.ToDecimal(sampleVaule.value);
-                                    var task = meterValueDbService.InsertAsync(
+                                    datas.Add(new InsertMeterValueParam(
                                         chargeBoxId: session.ChargeBoxId
                                         , connectorId: (byte)_request.connectorId
                                         , value: value
@@ -426,15 +414,29 @@ internal partial class ProfileHandler
                                         , phaseId: sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0
                                         , locationId: sampleVaule.location.HasValue ? (int)sampleVaule.location : 0
                                         , unitId: sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0
-                                        , transactionId: _request.transactionId.HasValue ? _request.transactionId.Value : -1);
+                                        , transactionId: _request.transactionId.HasValue ? _request.transactionId.Value : -1));
+                                    //var task = meterValueDbService.InsertAsync(
+                                    //    chargeBoxId: session.ChargeBoxId
+                                    //    , connectorId: (byte)_request.connectorId
+                                    //    , value: value
+                                    //    , createdOn: item.timestamp
+                                    //    , contextId: sampleVaule.context.HasValue ? (int)sampleVaule.context : 0
+                                    //    , formatId: sampleVaule.format.HasValue ? (int)sampleVaule.format : 0
+                                    //    , measurandId: sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0
+                                    //    , phaseId: sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0
+                                    //    , locationId: sampleVaule.location.HasValue ? (int)sampleVaule.location : 0
+                                    //    , unitId: sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0
+                                    //    , transactionId: _request.transactionId.HasValue ? _request.transactionId.Value : -1);
 
                                     //var task = Task.Delay(2_000); 
-                                    insertTasks.Add(task);
+                                    //insertTasks.Add(task);
                                 }
                             }
-                            insertTasksCnt = insertTasks.Count;
+                            //insertTasksCnt = insertTasks.Count;
+                            insertTasksCnt = datas.Count;
                             s3 = meterValueTimer.ElapsedMilliseconds;
-                            await Task.WhenAll(insertTasks);
+                            //await Task.WhenAll(insertTasks);
+                            await meterValueDbService.InsertBundleAsync(datas);
                             s4 = meterValueTimer.ElapsedMilliseconds;
                         }
 
@@ -664,11 +666,6 @@ internal partial class ProfileHandler
 
                             TransactionRecord transaction;
                             transaction = await mainDbService.GetTransactionForStopTransaction(_request.transactionId, session.ChargeBoxId);
-                            //using (var db = await maindbContextFactory.CreateDbContextAsync())
-                            //{
-                            //    transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
-                            //     && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
-                            //}
 
                             if (transaction is null)
                             {
@@ -701,24 +698,6 @@ internal partial class ProfileHandler
                                     receipt: string.Empty,
                                     cost: session.IsBilling ? -1 : 0);
 
-                                //using (var db = await maindbContextFactory.CreateDbContextAsync())
-                                //{
-                                //    var _transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
-                                //     && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
-
-                                //    _transaction.MeterStop = _request.meterStop;
-                                //    _transaction.StopTime = _request.timestamp.ToUniversalTime();
-                                //    _transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
-                                //    _transaction.StopReason = _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString();
-                                //    _transaction.StopIdTag = _request.idTag;
-                                //    _transaction.Receipt = string.Empty;
-                                //    _transaction.Cost = session.IsBilling ? -1 : 0;
-
-                                //    //await db.SaveChangesAsync();
-                                //    await db.SaveChangesAsync();
-                                //}
-
-
                                 if (_request.transactionData != null && _request.transactionData.Count > 0)
                                 {
                                     _request.transactionData[0].sampledValue.Add(new SampledValue()
@@ -759,30 +738,43 @@ internal partial class ProfileHandler
                             if (_request.transactionData != null &&
                                 _request.transactionData.Count > 0)
                             {
-                                List<Task> insertTasks = new();
+                                //List<Task> insertTasks = new();
+                                List<InsertMeterValueParam> datas = new();
                                 foreach (var item in _request.transactionData)
                                 {
                                     foreach (var sampleVaule in item.sampledValue)
                                     {
                                         decimal value = Convert.ToDecimal(sampleVaule.value);
-                                        var task = meterValueDbService.InsertAsync(
+                                        datas.Add(new InsertMeterValueParam(
                                             chargeBoxId: session.ChargeBoxId
-                                                , connectorId: (byte)_ConnectorId
-                                                , value: value
-                                                , createdOn: item.timestamp
-                                                , contextId: sampleVaule.context.HasValue ? (int)sampleVaule.context : 0
-                                                , formatId: sampleVaule.format.HasValue ? (int)sampleVaule.format : 0
-                                                , measurandId: sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0
-                                                , phaseId: sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0
-                                                , locationId: sampleVaule.location.HasValue ? (int)sampleVaule.location : 0
-                                                , unitId: sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0
-                                                , transactionId: _request.transactionId);
-                                        insertTasks.Add(task);
+                                            , connectorId: (byte)_ConnectorId
+                                            , value: value
+                                            , createdOn: item.timestamp
+                                            , contextId: sampleVaule.context.HasValue ? (int)sampleVaule.context : 0
+                                            , formatId: sampleVaule.format.HasValue ? (int)sampleVaule.format : 0
+                                            , measurandId: sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0
+                                            , phaseId: sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0
+                                            , locationId: sampleVaule.location.HasValue ? (int)sampleVaule.location : 0
+                                            , unitId: sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0
+                                            , transactionId: _request.transactionId));
+                                        //var task = meterValueDbService.InsertAsync(
+                                        //    chargeBoxId: session.ChargeBoxId
+                                        //        , connectorId: (byte)_ConnectorId
+                                        //        , value: value
+                                        //        , createdOn: item.timestamp
+                                        //        , contextId: sampleVaule.context.HasValue ? (int)sampleVaule.context : 0
+                                        //        , formatId: sampleVaule.format.HasValue ? (int)sampleVaule.format : 0
+                                        //        , measurandId: sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0
+                                        //        , phaseId: sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0
+                                        //        , locationId: sampleVaule.location.HasValue ? (int)sampleVaule.location : 0
+                                        //        , unitId: sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0
+                                        //        , transactionId: _request.transactionId);
+                                        //insertTasks.Add(task);
                                     }
                                 }
-                                await Task.WhenAll(insertTasks);
+                                //await Task.WhenAll(insertTasks);
+                                await meterValueDbService.InsertBundleAsync(datas);
                             }
-
                             #endregion
 
                             meterValueTime = watch.ElapsedMilliseconds;

+ 1 - 0
EVCB_OCPP.WSServer/Program.cs

@@ -17,6 +17,7 @@ using Microsoft.Data.SqlClient;
 using EVCB_OCPP.WSServer.Helper;
 using Quartz;
 using EVCB_OCPP.WSServer.Jobs;
+using Microsoft.AspNetCore.Builder;
 
 namespace EVCB_OCPP.WSServer
 {

+ 22 - 23
EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs

@@ -41,7 +41,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         //connectionLogdbConnectionString = configuration.GetConnectionString("MeterValueDBContext");
 
         var opLimit = GetLimit(configuration);
-        this.queueHandler = new(WriteMachineLog, opLimit);
+        this.queueHandler = new(WriteMachineLogEF, opLimit);
 
         InitInsertConnectonLogHandler();
     }
@@ -75,9 +75,9 @@ public class ConnectionLogdbService : IConnectionLogdbService
     {
         var log = new MachineLog(clientData, data, messageType, errorMsg, isSent);
         //queueHandler.Enqueue(log);
-        //WriteMachineLog(log);
-        insertConnectonLogHandler.HandleAsync(log);
-        //_ = InsertWithDapper(log);
+        //WriteMachineLogEF(log);
+        //insertConnectonLogHandler.HandleAsync(log);
+        _ = InsertWithDapper(log);
     }
 
     private async Task InsertWithDapper(MachineLog log)
@@ -88,7 +88,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         if (!await GetTableExist(workTime))
         {
             t0 = watch.ElapsedMilliseconds;
-            await WriteMachineLog(log);
+            await WriteMachineLogEF(log);
             watch.Stop();
             t1 = watch.ElapsedMilliseconds;
             if (t1 > 500)
@@ -144,7 +144,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
             );
     }
 
-    private async Task WriteMachineLog(MachineLog log)
+    private async Task WriteMachineLogEF(MachineLog log)
     {
         var watcher = Stopwatch.StartNew();
 
@@ -156,23 +156,22 @@ public class ConnectionLogdbService : IConnectionLogdbService
             {
                 logger.LogCritical(log.clientData.Path + "]********************session ChargeBoxId null sessionId=" + log.clientData.SessionID);
             }
+            string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
+                  "@ChargeBoxId,@MessageType,@Data,@Msg,@IsSent,@EVSEEndPoint,@Session";
+            var dd = DateTime.UtcNow;
+            SqlParameter[] parameter =
+            {
+                    new SqlParameter("CreatedOn", SqlDbType.DateTime){ Value = dd },
+                    new SqlParameter("ChargeBoxId", SqlDbType.NVarChar, 50){ Value= log.clientData.ChargeBoxId==null?"unknown":log.clientData.ChargeBoxId.Replace("'","''") },
+                    new SqlParameter("MessageType", SqlDbType.NVarChar , 50){ Value =  log.messageType.Replace("'","''")},
+                    new SqlParameter("Data", SqlDbType.NVarChar, -1) { Value = log.data.Replace("'", "''") },
+                    new SqlParameter("Msg", SqlDbType.NVarChar, 200) { Value = log.errorMsg.Replace("'", "''") },
+                    new  SqlParameter("IsSent", SqlDbType.Bit) { Value = log.isSent },
+                    new  SqlParameter("EVSEEndPoint", SqlDbType.NVarChar, 25) { Value = log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString() },
+                    new  SqlParameter("Session", SqlDbType.NVarChar, 36) { Value = log.clientData.SessionID == null ? "123" : log.clientData.SessionID }
+            };
             using (var db = await connectionLogdbContextFactory.CreateDbContextAsync())
             {
-                string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
-                      "@ChargeBoxId,@MessageType,@Data,@Msg,@IsSent,@EVSEEndPoint,@Session";
-                var dd = DateTime.UtcNow;
-                SqlParameter[] parameter =
-                {
-                           new SqlParameter("CreatedOn", SqlDbType.DateTime){ Value = dd },
-                           new SqlParameter("ChargeBoxId", SqlDbType.NVarChar, 50){ Value= log.clientData.ChargeBoxId==null?"unknown":log.clientData.ChargeBoxId.Replace("'","''") },
-                           new SqlParameter("MessageType", SqlDbType.NVarChar , 50){ Value =  log.messageType.Replace("'","''")},
-                           new SqlParameter("Data", SqlDbType.NVarChar, -1) { Value = log.data.Replace("'", "''") },
-                           new SqlParameter("Msg", SqlDbType.NVarChar, 200) { Value = log.errorMsg.Replace("'", "''") },
-                           new  SqlParameter("IsSent", SqlDbType.Bit) { Value = log.isSent },
-                           new  SqlParameter("EVSEEndPoint", SqlDbType.NVarChar, 25) { Value = log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString() },
-                           new  SqlParameter("Session", SqlDbType.NVarChar, 36) { Value = log.clientData.SessionID == null ? "123" : log.clientData.SessionID }
-                    };
-
                 await db.Database.ExecuteSqlRawAsync(sp, parameter);
             }
         }
@@ -204,7 +203,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         if (!await GetTableExist(workTime))
         {
             t0 = watch.ElapsedMilliseconds;
-            await WriteMachineLog(candidate);
+            await WriteMachineLogEF(candidate);
             watch.Stop();
             t1 = watch.ElapsedMilliseconds;
             if (t1 > 500)
@@ -269,7 +268,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         if (!await GetTableExist(workTime))
         {
             var candidate = parmsList.First();
-            await WriteMachineLog(candidate);
+            await WriteMachineLogEF(candidate);
             parmsList.Remove(candidate);
         }
 

+ 0 - 2
EVCB_OCPP.WSServer/Service/LoadingBalanceService.cs

@@ -245,8 +245,6 @@ namespace EVCB_OCPP.WSServer.Service
             return machineIds;
         }
 
-
-
         async private Task<Dictionary<string, decimal?>> GetAveragePower(int stationId, int availableCapacity)
         {
             Dictionary<string, decimal?> dic = new Dictionary<string, decimal?>();

+ 427 - 232
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -2,14 +2,11 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.WSServer.Helper;
-using log4net.Core;
-using Microsoft.AspNetCore.Http.HttpResults;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
-using MongoDB.Driver.Core.Connections;
 using Newtonsoft.Json;
 using OCPPPackage.Profiles;
 using System.Data;
@@ -39,6 +36,7 @@ public interface IMainDbService
     Task<int> AddNewTransactionRecord(TransactionRecord newTransaction);
     Task<TransactionRecord> GetTransactionForStopTransaction(int transactionId, string chargeBoxId);
     Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost);
+    Task<bool> UpdateHeartBeats(IEnumerable<Machine> heartBeatsData);
 }
 
 public class MainDbService : IMainDbService
@@ -48,12 +46,14 @@ public class MainDbService : IMainDbService
         SqlConnectionFactory<MainDBContext> sqlConnectionFactory,
         IMemoryCache memoryCache,
         IConfiguration configuration,
-        ILoggerFactory loggerFactory)
+        ILoggerFactory loggerFactory,
+        ILogger<MainDbService> logger)
     {
         this.contextFactory = contextFactory;
         this.sqlConnectionFactory = sqlConnectionFactory;
         this.memoryCache = memoryCache;
         this.loggerFactory = loggerFactory;
+        this.logger = logger;
         var startupLimit = GetStartupLimit(configuration);
         //this.connectionString = configuration.GetConnectionString("MainDBContext");
         this.startupSemaphore = new(startupLimit);
@@ -66,10 +66,15 @@ public class MainDbService : IMainDbService
         InitAddServerMessageHandler();
     }
 
+    private const string CustomerMemCacheKeyFromat = "Customer_{0}";
+    private const string ChargeBoxConnectorIdMemCacheKeyFromat = "Connector_{0}{1}";
+
     private readonly IDbContextFactory<MainDBContext> contextFactory;
     private readonly SqlConnectionFactory<MainDBContext> sqlConnectionFactory;
     private readonly IMemoryCache memoryCache;
     private readonly ILoggerFactory loggerFactory;
+    private readonly ILogger<MainDbService> logger;
+
     //private string connectionString;
     private readonly QueueSemaphore startupSemaphore;
     private readonly SemaphoreSlim opSemaphore;
@@ -81,6 +86,7 @@ public class MainDbService : IMainDbService
     {
         using var semaphoreWrapper = await startupSemaphore.GetToken();
         using var db = await contextFactory.CreateDbContextAsync();
+
         var machine = await db.Machine.Where(x => x.ChargeBoxId == ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefaultAsync();
         if (machine == null)
         {
@@ -101,42 +107,25 @@ public class MainDbService : IMainDbService
             .Select(x => x.ConfigureSetting).FirstOrDefaultAsync();
     }
 
-    public async Task<string> GetMachineSecurityProfile(string ChargeBoxId)
+    public Task<string> GetMachineSecurityProfile(string ChargeBoxId)
     {
-        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile);
+        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile);
     }
 
-    public async Task<string> GetMachineAuthorizationKey(string ChargeBoxId)
+    public Task<string> GetMachineAuthorizationKey(string ChargeBoxId)
     {
-        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey);
+        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey);
     }
 
-    public async Task<string> GetMachineHeartbeatInterval(string ChargeBoxId)
+    public Task<string> GetMachineHeartbeatInterval(string ChargeBoxId)
     {
-        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.HeartbeatInterval);
+        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.HeartbeatInterval);
     }
 
-    public async Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
+    public Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
     {
-        //using var semaphoreWrapper = await startupSemaphore.GetToken();
-        //using var db = await contextFactory.CreateDbContextAsync();
-
-        //var _machine = await db.Machine.FirstOrDefaultAsync(x => x.ChargeBoxId == ChargeBoxId);
-        //_machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
-        //_machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
-        //_machine.ChargePointModel = machine.ChargePointModel;
-        //_machine.ChargePointVendor = machine.ChargePointVendor;
-        //_machine.FW_CurrentVersion = machine.FW_CurrentVersion;
-        //_machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
-        //_machine.Imsi = machine.Imsi;
-        //_machine.MeterSerialNumber = machine.MeterSerialNumber;
-        //_machine.MeterType = machine.MeterType;
-
-        //await db.SaveChangesAsync();
-
-        //using var semaphoreWrapper = await startupSemaphore.GetToken();
-
-        await updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
+        //return UpdateMachineBasicInfoEF(ChargeBoxId, machine);
+        return updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
     }
 
     public async Task AddOCMF(OCMF oCMF)
@@ -167,16 +156,19 @@ public class MainDbService : IMainDbService
 
         await db.SaveChangesAsync();
 
-        memoryCache.Set($"{ChargeBoxId}{ConnectorId}", _currentStatus, TimeSpan.FromHours(12));
+        memoryCache.Set(
+            string.Format(ChargeBoxConnectorIdMemCacheKeyFromat, ChargeBoxId, ConnectorId)
+            , _currentStatus, TimeSpan.FromHours(12));
     }
 
     public async ValueTask<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId)
     {
-        var key = $"{ChargeBoxId}{ConnectorId}";
+        var key = string.Format(ChargeBoxConnectorIdMemCacheKeyFromat, ChargeBoxId, ConnectorId);
         if (memoryCache.TryGetValue<ConnectorStatus>(key, out var status))
         {
             return status;
         }
+
         using var db = await contextFactory.CreateDbContextAsync();
         var statusFromDb = await db.ConnectorStatus.Where(x => x.ChargeBoxId == ChargeBoxId
                             && x.ConnectorId == ConnectorId).AsNoTracking().FirstOrDefaultAsync();
@@ -191,143 +183,29 @@ public class MainDbService : IMainDbService
         //await UpdateConnectorStatusEF(Id, Status);
         await UpdateConnectorStatusDapper(Id, Status);
 
-        var key = $"{Status.ChargeBoxId}{Status.ConnectorId}";
+        var key = string.Format(ChargeBoxConnectorIdMemCacheKeyFromat, Status.ChargeBoxId, Status.ConnectorId);
         memoryCache.Set(key, Status, TimeSpan.FromHours(12));
         return;
     }
 
-    public async Task<Guid> GetCustomerIdByChargeBoxId(string chargeboxId)
+    public Task<Guid> GetCustomerIdByChargeBoxId(string chargeboxId)
     {
-        //using var db = await contextFactory.CreateDbContextAsync();
-        //var _CustomerId = await db.Machine.Where(x => x.ChargeBoxId == chargeboxId).Select(x => x.CustomerId).FirstOrDefaultAsync();
-        //return _CustomerId;
-        var parameters = new DynamicParameters();
-        parameters.Add("@ChargeBoxId", chargeboxId, DbType.String, ParameterDirection.Input, 50);
-
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        var _existedTx = await conn.QueryFirstOrDefaultAsync<Guid>("""
-            select CustomerId
-            from dbo.Machine
-            where
-            ChargeBoxId = @ChargeBoxId
-            """, parameters);
-
-        return _existedTx;
+        //return GetCustomerIdByChargeBoxIdEF(chargeboxId);
+        return GetCustomerIdByChargeBoxIdDapper(chargeboxId);
     }
 
-    public async Task<int?> TryGetDuplicatedTransactionId(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp)
+    public Task<int?> TryGetDuplicatedTransactionId(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp)
     {
-        //var _existedTx = await db.TransactionRecord.Where(x => x.CustomerId == customerId && x.ChargeBoxId == chargeBoxId
-        //                       && x.ConnectorId == connectorId && x.StartTime == timestamp).Select(x=>x.Id).fi();
-        var parameters = new DynamicParameters();
-        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
-        parameters.Add("@CustomerId", customerId, DbType.Guid, ParameterDirection.Input);
-        parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);
-        parameters.Add("@TimeStamp", timestamp, DbType.DateTime, ParameterDirection.Input);
-
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        var _existedTx = await conn.QueryFirstOrDefaultAsync<int?>("""
-            SELECT Id
-            FROM dbo.TransactionRecord
-            WHERE
-            ChargeBoxId = @ChargeBoxId and
-            CustomerId = @CustomerId and
-            ConnectorId = @ConnectorId and
-            StartTime = @TimeStamp
-            """, parameters);
-
-        return _existedTx;
+        //return TryGetDuplicatedTransactionIdEF(chargeBoxId, customerId, connectorId, timestamp);
+        return TryGetDuplicatedTransactionIdDapper(chargeBoxId, customerId, connectorId, timestamp);
     }
 
-    private async Task UpdateConnectorStatusEF(string Id, ConnectorStatus Status)
-    {
-        using var db = await contextFactory.CreateDbContextAsync();
 
-        ConnectorStatus status = new() { Id = Id };
-
-        db.ChangeTracker.AutoDetectChangesEnabled = false;
-        db.ConnectorStatus.Attach(status);
-
-
-        status.CreatedOn = Status.CreatedOn;
-        status.Status = Status.Status;
-        status.ChargePointErrorCodeId = Status.ChargePointErrorCodeId;
-        status.ErrorInfo = Status.ErrorInfo;
-        status.VendorId = Status.VendorId;
-        status.VendorErrorCode = Status.VendorErrorCode;
-
-
-        db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
-        db.Entry(status).Property(x => x.Status).IsModified = true;
-        db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
-        db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
-        db.Entry(status).Property(x => x.VendorId).IsModified = true;
-        db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
-
-        await db.SaveChangesAsync();
-    }
-
-    private async Task UpdateConnectorStatusDapper(string Id, ConnectorStatus Status)
+    public ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId,
+        int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId)
     {
-        var parameters = new DynamicParameters();
-        parameters.Add("@Id", Id, DbType.String, ParameterDirection.Input, 36);
-        parameters.Add("@CreatedOn", Status.CreatedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@Status", Status.Status, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@ChargePointErrorCodeId", Status.ChargePointErrorCodeId, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@ErrorInfo", Status.ErrorInfo, DbType.String, ParameterDirection.Input, 50);
-        parameters.Add("@VendorId", Status.VendorId, DbType.String, ParameterDirection.Input, 255);
-        parameters.Add("@VendorErrorCode", Status.VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
-
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        await conn.ExecuteAsync("""
-            update ConnectorStatus
-            set
-            CreatedOn = @CreatedOn,
-            Status = @Status,
-            ChargePointErrorCodeId = @ChargePointErrorCodeId,
-            ErrorInfo = @ErrorInfo,
-            VendorId = @VendorId,
-            VendorErrorCode = @VendorErrorCode
-            where Id = @Id
-            """, parameters );
-    }
-
-    public async ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId,
-    int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId)
-    {
-        //using var db = await contextFactory.CreateDbContextAsync();
-        //await db.MachineError.AddAsync(new MachineError()
-        //{
-        //    ConnectorId = ConnectorId,
-        //    CreatedOn = CreatedOn,
-        //    Status = Status,
-        //    ChargeBoxId = ChargeBoxId,
-        //    ErrorCodeId = ErrorCodeId,
-        //    ErrorInfo = ErrorInfo,
-        //    PreStatus = PreStatus,
-        //    VendorErrorCode = VendorErrorCode,
-        //    VendorId = VendorId
-        //});
-
-        //await db.SaveChangesAsync();
-
-        var parameters = new DynamicParameters();
-        parameters.Add("@ConnectorId", ConnectorId, DbType.Int16, ParameterDirection.Input);
-        parameters.Add("@PreStatus", PreStatus, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@Status", Status, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@ErrorInfo", ErrorInfo, DbType.String, ParameterDirection.Input, 50);
-        parameters.Add("@VendorId", VendorId, DbType.String, ParameterDirection.Input, 255);
-        parameters.Add("@CreatedOn", CreatedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@ErrorCodeId", ErrorCodeId, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@VendorErrorCode", VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
-        parameters.Add("@ChargeBoxId", ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
-
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        await conn.ExecuteAsync("""
-            INSERT INTO MachineError
-            (ConnectorId, PreStatus, Status, ErrorInfo, VendorId, CreatedOn, ErrorCodeId, VendorErrorCode, ChargeBoxId)
-            VALUES (@ConnectorId, @PreStatus, @Status, @ErrorInfo, @VendorId, @CreatedOn, @ErrorCodeId, @VendorErrorCode, @ChargeBoxId)
-            """, parameters);
+        //return AddMachineErrorEF(ConnectorId, CreatedOn, Status, ChargeBoxId, ErrorCodeId, ErrorInfo, PreStatus, VendorErrorCode, VendorId);
+        return AddMachineErrorDapper(ConnectorId, CreatedOn, Status, ChargeBoxId, ErrorCodeId, ErrorInfo, PreStatus, VendorErrorCode, VendorId);
     }
 
     public Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
@@ -355,7 +233,7 @@ public class MainDbService : IMainDbService
                 });
         }
 
-        return AddServerMessage(new ServerMessage()
+        var data = new ServerMessage()
         {
             ChargeBoxId = ChargeBoxId,
             CreatedBy = CreatedBy,
@@ -364,20 +242,24 @@ public class MainDbService : IMainDbService
             OutRequest = _OutRequest,
             SerialNo = SerialNo,
             InMessage = InMessage
-        });
+        };
+
+        return AddServerMessage(data);
     }
 
     public Task AddServerMessage(ServerMessage message)
     {
         //return AddServerMessageEF(message);
-        return addServerMessageHandler.HandleAsync(message);
+        //return addServerMessageHandler.HandleAsync(message);
+        return AddServerMessageDapper(message);
     }
 
     public ValueTask<Customer> GetCustomer(string id)
         => GetCustomer(new Guid(id));
+
     public async ValueTask<Customer> GetCustomer(Guid id)
     {
-        var key = $"Customer{id}";
+        var key = string.Format(CustomerMemCacheKeyFromat, id);
         if (memoryCache.TryGetValue<Customer>(key, out var customer))
         {
             return customer;
@@ -397,62 +279,44 @@ public class MainDbService : IMainDbService
         return toReturn;
     }
 
-    public async Task<int> AddNewTransactionRecord(TransactionRecord newTransaction)
+    public Task<int> AddNewTransactionRecord(TransactionRecord newTransaction)
     {
-        //using (var db = await contextFactory.CreateDbContextAsync())
-        //{
-        //    await db.TransactionRecord.AddAsync(newTransaction);
-
-        //    await db.SaveChangesAsync();
-
-        //   return newTransaction.Id;
-        //}
+        //return AddNewTransactionRecordEF(newTransaction);
+        return AddNewTransactionRecordDapper(newTransaction);
+    }
 
-        var parameters = new DynamicParameters();
-        parameters.Add("@ChargeBoxId", newTransaction.ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
-        parameters.Add("@ConnectorId", newTransaction.ConnectorId, DbType.Int16, ParameterDirection.Input);
-        parameters.Add("@CreatedOn", newTransaction.CreatedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@UpdatedOn", newTransaction.UpdatedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@StartTransactionReportedOn", newTransaction.StartTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@StopTransactionReportedOn", newTransaction.StopTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@StartIdTag", newTransaction.StartIdTag, DbType.String, ParameterDirection.Input, 20);
-        parameters.Add("@MeterStart", newTransaction.MeterStart, DbType.Decimal, ParameterDirection.Input, precision:18, scale:2);
-        parameters.Add("@MeterStop", newTransaction.MeterStop, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
-        parameters.Add("@CustomerId", newTransaction.CustomerId, DbType.Guid, ParameterDirection.Input);
-        parameters.Add("@StartTime", newTransaction.StartTime, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@StopTime", newTransaction.StopTime, DbType.DateTime, ParameterDirection.Input);
-        parameters.Add("@ReservationId", newTransaction.ReservationId, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@RetryStartTransactionTimes", newTransaction.RetryStartTransactionTimes, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@RetryStopTransactionTimes", newTransaction.RetryStopTransactionTimes, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@Fee", newTransaction.Fee, DbType.String, ParameterDirection.Input, 1500);
+    public Task<TransactionRecord> GetTransactionForStopTransaction(int transactionId, string chargeBoxId)
+    {
+        //return GetTransactionForStopTransactionEF(transactionId, chargeBoxId);
+        return GetTransactionForStopTransactionDapper(transactionId, chargeBoxId);
+    }
 
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        var id = await conn.QuerySingleAsync<int>("""
-            INSERT INTO TransactionRecord
-            (ChargeBoxId, ConnectorId, CreatedOn, UpdatedOn, StartTransactionReportedOn, StopTransactionReportedOn,
-            StartIdTag, MeterStart, MeterStop, CustomerId, StartTime, StopTime, ReservationId, RetryStartTransactionTimes, RetryStopTransactionTimes, Fee)
-            OUTPUT INSERTED.Id
-            VALUES (@ChargeBoxId, @ConnectorId, @CreatedOn, @UpdatedOn, @StartTransactionReportedOn, @StopTransactionReportedOn,
-            @StartIdTag, @MeterStart, @MeterStop, @CustomerId, @StartTime, @StopTime, @ReservationId, @RetryStartTransactionTimes, @RetryStopTransactionTimes, @Fee)
-            """, parameters);
-        return id;
+    public Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
+    {
+        //return UpdateTransactionEF(transactionId, meterStop, stopTime, stopReasonId, stopReason, stopIdTag, receipt, cost);
+        return UpdateTransactionDapper(transactionId, meterStop, stopTime, stopReasonId, stopReason, stopIdTag, receipt, cost);
     }
 
-    public async Task<TransactionRecord> GetTransactionForStopTransaction(int transactionId, string chargeBoxId)
+    private async Task UpdateTransactionEF(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
     {
-        var parameters = new DynamicParameters();
-        parameters.Add("@TransactionId", transactionId, DbType.Int32, ParameterDirection.Input);
-        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        using var db = await contextFactory.CreateDbContextAsync();
 
-        using var conn = await sqlConnectionFactory.CreateAsync();
-        var record = await conn.QuerySingleAsync<TransactionRecord>("""
-            SELECT ConnectorId, MeterStop, MeterStart, StopTime FROM TransactionRecord
-            WHERE Id = @TransactionId and ChargeBoxId = @ChargeBoxId 
-            """, parameters);
-        return record;
+        var _transaction = db.TransactionRecord.Where(x => x.Id == transactionId //&& x.ChargeBoxId == session.ChargeBoxId
+            ).FirstOrDefault();
+
+        _transaction.MeterStop = meterStop;
+        _transaction.StopTime = stopTime;
+        _transaction.StopReasonId = stopReasonId;
+        _transaction.StopReason = stopReason;
+        _transaction.StopIdTag = stopIdTag;
+        _transaction.Receipt = receipt;
+        _transaction.Cost = cost;
+
+        //await db.SaveChangesAsync();
+        await db.SaveChangesAsync();
     }
 
-    public async Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
+    private async Task UpdateTransactionDapper(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
     {
         var parameters = new DynamicParameters();
         parameters.Add("@TransactionId", transactionId, DbType.Int32, ParameterDirection.Input);
@@ -478,6 +342,35 @@ public class MainDbService : IMainDbService
         return;
     }
 
+    public Task<bool> UpdateHeartBeats(IEnumerable<Machine> heartBeatsData)
+    {
+        //return UpdateHeartBeatsEF(heartBeatsData);
+        return UpdateHeartBeatsDapper(heartBeatsData);
+    }
+    private void InitUpdateConnectorStatusHandler()
+    {
+        if (statusNotificationHandler is not null)
+        {
+            throw new Exception($"{nameof(InitUpdateConnectorStatusHandler)} should only called once");
+        }
+
+        statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(
+            handleFunc: BundleUpdateConnectorStatusDapper,
+            logger: loggerFactory.CreateLogger("StatusNotificationHandler"),
+            workerCnt: 1);
+    }
+    private void InitAddServerMessageHandler()
+    {
+        if (addServerMessageHandler is not null)
+        {
+            throw new Exception($"{nameof(InitAddServerMessageHandler)} should only called once");
+        }
+
+        addServerMessageHandler = new GroupSingleHandler<ServerMessage>(
+            handleFunc: BundleAddServerMessage,
+            logger: loggerFactory.CreateLogger("AddServerMessageHandler"));
+    }
+
     private void InitUpdateMachineBasicInfoHandler()
     {
         if (updateMachineBasicInfoHandler is not null)
@@ -491,6 +384,27 @@ public class MainDbService : IMainDbService
             workerCnt: 10);
     }
 
+    private async Task UpdateMachineBasicInfoEF(string chargeBoxId, Machine machine)
+    {
+        using var semaphoreWrapper = await startupSemaphore.GetToken();
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        var _machine = await db.Machine.FirstOrDefaultAsync(x => x.ChargeBoxId == chargeBoxId);
+        _machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
+        _machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
+        _machine.ChargePointModel = machine.ChargePointModel;
+        _machine.ChargePointVendor = machine.ChargePointVendor;
+        _machine.FW_CurrentVersion = machine.FW_CurrentVersion;
+        _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
+        _machine.Imsi = machine.Imsi;
+        _machine.MeterSerialNumber = machine.MeterSerialNumber;
+        _machine.MeterType = machine.MeterType;
+
+        await db.SaveChangesAsync();
+
+        //using var semaphoreWrapper = await startupSemaphore.GetToken();
+    }
+
     private async Task BundelUpdateMachineBasicInfo(IEnumerable<UpdateMachineBasicInfoParam> pams)
     {
         using var db = await contextFactory.CreateDbContextAsync();
@@ -513,20 +427,153 @@ public class MainDbService : IMainDbService
         }
 
         await db.SaveChangesAsync();
-        trans.Commit();
+        await trans.CommitAsync();
     }
 
-    private void InitUpdateConnectorStatusHandler()
+    private async Task UpdateConnectorStatusEF(string Id, ConnectorStatus Status)
     {
-        if (statusNotificationHandler is not null)
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        ConnectorStatus status = new() { Id = Id };
+
+        db.ChangeTracker.AutoDetectChangesEnabled = false;
+        db.ConnectorStatus.Attach(status);
+
+
+        status.CreatedOn = Status.CreatedOn;
+        status.Status = Status.Status;
+        status.ChargePointErrorCodeId = Status.ChargePointErrorCodeId;
+        status.ErrorInfo = Status.ErrorInfo;
+        status.VendorId = Status.VendorId;
+        status.VendorErrorCode = Status.VendorErrorCode;
+
+
+        db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
+        db.Entry(status).Property(x => x.Status).IsModified = true;
+        db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
+        db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
+        db.Entry(status).Property(x => x.VendorId).IsModified = true;
+        db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
+
+        await db.SaveChangesAsync();
+    }
+
+    private async Task UpdateConnectorStatusDapper(string Id, ConnectorStatus Status)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@Id", Id, DbType.String, ParameterDirection.Input, 36);
+        parameters.Add("@CreatedOn", Status.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@Status", Status.Status, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ChargePointErrorCodeId", Status.ChargePointErrorCodeId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ErrorInfo", Status.ErrorInfo, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@VendorId", Status.VendorId, DbType.String, ParameterDirection.Input, 255);
+        parameters.Add("@VendorErrorCode", Status.VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        await conn.ExecuteAsync("""
+            update ConnectorStatus
+            set
+            CreatedOn = @CreatedOn,
+            Status = @Status,
+            ChargePointErrorCodeId = @ChargePointErrorCodeId,
+            ErrorInfo = @ErrorInfo,
+            VendorId = @VendorId,
+            VendorErrorCode = @VendorErrorCode
+            where Id = @Id
+            """, parameters);
+    }
+
+    private async Task<Guid> GetCustomerIdByChargeBoxIdEF(string chargeboxId)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        var _CustomerId = await db.Machine.Where(x => x.ChargeBoxId == chargeboxId).Select(x => x.CustomerId).FirstOrDefaultAsync();
+        return _CustomerId;
+    }
+
+    private async Task<Guid> GetCustomerIdByChargeBoxIdDapper(string chargeboxId)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", chargeboxId, DbType.String, ParameterDirection.Input, 50);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var _existedTx = await conn.QueryFirstOrDefaultAsync<Guid>("""
+            select CustomerId
+            from dbo.Machine
+            where
+            ChargeBoxId = @ChargeBoxId
+            """, parameters);
+
+        return _existedTx;
+    }
+
+    private async Task<int?> TryGetDuplicatedTransactionIdEF(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        var _existedTx = await db.TransactionRecord.Where(x => x.CustomerId == customerId && x.ChargeBoxId == chargeBoxId
+                               && x.ConnectorId == connectorId && x.StartTime == timestamp).Select(x => x.Id).FirstOrDefaultAsync();
+        return _existedTx;
+    }
+
+    private async Task<int?> TryGetDuplicatedTransactionIdDapper(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@CustomerId", customerId, DbType.Guid, ParameterDirection.Input);
+        parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@TimeStamp", timestamp, DbType.DateTime, ParameterDirection.Input);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var _existedTx = await conn.QueryFirstOrDefaultAsync<int?>("""
+            SELECT Id
+            FROM dbo.TransactionRecord
+            WHERE
+            ChargeBoxId = @ChargeBoxId and
+            CustomerId = @CustomerId and
+            ConnectorId = @ConnectorId and
+            StartTime = @TimeStamp
+            """, parameters);
+
+        return _existedTx;
+    }
+
+    private async ValueTask AddMachineErrorEF(byte connectorId, DateTime createdOn, int status, string chargeBoxId, int errorCodeId, string errorInfo, int preStatus, string vendorErrorCode, string vendorId)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        await db.MachineError.AddAsync(new MachineError()
         {
-            throw new Exception($"{nameof(InitUpdateConnectorStatusHandler)} should only called once");
-        }
+            ConnectorId = connectorId,
+            CreatedOn = createdOn,
+            Status = status,
+            ChargeBoxId = chargeBoxId,
+            ErrorCodeId = errorCodeId,
+            ErrorInfo = errorInfo,
+            PreStatus = preStatus,
+            VendorErrorCode = vendorErrorCode,
+            VendorId = vendorId
+        });
 
-        statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(
-            handleFunc: BundleUpdateConnectorStatusDapper,
-            logger: loggerFactory.CreateLogger("StatusNotificationHandler"),
-            workerCnt: 1);
+        await db.SaveChangesAsync();
+    }
+
+    private async ValueTask AddMachineErrorDapper(byte connectorId, DateTime createdOn, int status, string chargeBoxId, int errorCodeId, string errorInfo, int preStatus, string vendorErrorCode, string vendorId)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@PreStatus", preStatus, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@Status", status, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ErrorInfo", errorInfo, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@VendorId", vendorId, DbType.String, ParameterDirection.Input, 255);
+        parameters.Add("@CreatedOn", createdOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@ErrorCodeId", errorCodeId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@VendorErrorCode", vendorErrorCode, DbType.String, ParameterDirection.Input, 100);
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        await conn.ExecuteAsync("""
+            INSERT INTO MachineError
+            (ConnectorId, PreStatus, Status, ErrorInfo, VendorId, CreatedOn, ErrorCodeId, VendorErrorCode, ChargeBoxId)
+            VALUES (@ConnectorId, @PreStatus, @Status, @ErrorInfo, @VendorId, @CreatedOn, @ErrorCodeId, @VendorErrorCode, @ChargeBoxId)
+            """, parameters);
     }
 
     private async Task BundleUpdateConnectorStatus(IEnumerable<StatusNotificationParam> statusNotifications)
@@ -564,7 +611,7 @@ public class MainDbService : IMainDbService
 
         await db.SaveChangesAsync();
         await trans.CommitAsync();
-        db.ChangeTracker.Clear();
+        //db.ChangeTracker.Clear();
     }
 
 
@@ -600,18 +647,6 @@ public class MainDbService : IMainDbService
         return Task.CompletedTask;
     }
 
-    private void InitAddServerMessageHandler()
-    {
-        if (addServerMessageHandler is not null)
-        {
-            throw new Exception($"{nameof(InitAddServerMessageHandler)} should only called once");
-        }
-
-        addServerMessageHandler = new GroupSingleHandler<ServerMessage>(
-            handleFunc: BundleAddServerMessage,
-            logger: loggerFactory.CreateLogger("AddServerMessageHandler"));
-    }
-
     private async Task BundleAddServerMessage(IEnumerable<ServerMessage> messages)
     {
         using var db = await contextFactory.CreateDbContextAsync();
@@ -624,7 +659,7 @@ public class MainDbService : IMainDbService
 
         await db.SaveChangesAsync();
         await trans.CommitAsync();
-        db.ChangeTracker.Clear();
+        //db.ChangeTracker.Clear();
     }
 
     private async Task AddServerMessageEF(ServerMessage message)
@@ -636,7 +671,97 @@ public class MainDbService : IMainDbService
 
         await db.SaveChangesAsync();
         await trans.CommitAsync();
-        db.ChangeTracker.Clear();
+        //db.ChangeTracker.Clear();
+    }
+
+    private async Task AddServerMessageDapper(ServerMessage message)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@SerialNo", message.SerialNo, DbType.String, ParameterDirection.Input, 36);
+        parameters.Add("@OutAction", message.OutAction, DbType.String, ParameterDirection.Input, 30);
+        parameters.Add("@OutRequest", message.OutRequest, DbType.String, ParameterDirection.Input);
+        parameters.Add("@InMessage", message.InMessage, DbType.String, ParameterDirection.Input);
+        parameters.Add("@CreatedOn", message.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@CreatedBy", message.CreatedBy, DbType.String, ParameterDirection.Input, 36);
+        parameters.Add("@ReceivedOn", message.ReceivedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@ChargeBoxId", message.ChargeBoxId, DbType.String, ParameterDirection.Input, 30);
+        parameters.Add("@UpdatedOn", message.UpdatedOn, DbType.DateTime, ParameterDirection.Input);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var resultCnt = await conn.ExecuteAsync("""
+            INSERT INTO ServerMessage
+            (SerialNo, OutAction, OutRequest, InMessage, CreatedOn, CreatedBy, ReceivedOn, ChargeBoxId, UpdatedOn)
+            VALUES (@SerialNo, @OutAction, @OutRequest, @InMessage, @CreatedOn, @CreatedBy, @ReceivedOn, @ChargeBoxId, @UpdatedOn)
+            """, parameters);
+        if (resultCnt != 1)
+        {
+            throw new Exception("Insert failed");
+        }
+        return;
+    }
+
+    private async Task<int> AddNewTransactionRecordEF(TransactionRecord newTransaction)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        await db.TransactionRecord.AddAsync(newTransaction);
+
+        await db.SaveChangesAsync();
+
+        return newTransaction.Id;
+    }
+
+    private async Task<int> AddNewTransactionRecordDapper(TransactionRecord newTransaction)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", newTransaction.ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@ConnectorId", newTransaction.ConnectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@CreatedOn", newTransaction.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@UpdatedOn", newTransaction.UpdatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StartTransactionReportedOn", newTransaction.StartTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StopTransactionReportedOn", newTransaction.StopTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StartIdTag", newTransaction.StartIdTag, DbType.String, ParameterDirection.Input, 20);
+        parameters.Add("@MeterStart", newTransaction.MeterStart, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
+        parameters.Add("@MeterStop", newTransaction.MeterStop, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
+        parameters.Add("@CustomerId", newTransaction.CustomerId, DbType.Guid, ParameterDirection.Input);
+        parameters.Add("@StartTime", newTransaction.StartTime, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StopTime", newTransaction.StopTime, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@ReservationId", newTransaction.ReservationId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@RetryStartTransactionTimes", newTransaction.RetryStartTransactionTimes, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@RetryStopTransactionTimes", newTransaction.RetryStopTransactionTimes, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@Fee", newTransaction.Fee, DbType.String, ParameterDirection.Input, 1500);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var id = await conn.QuerySingleAsync<int>("""
+            INSERT INTO TransactionRecord
+            (ChargeBoxId, ConnectorId, CreatedOn, UpdatedOn, StartTransactionReportedOn, StopTransactionReportedOn,
+            StartIdTag, MeterStart, MeterStop, CustomerId, StartTime, StopTime, ReservationId, RetryStartTransactionTimes, RetryStopTransactionTimes, Fee)
+            OUTPUT INSERTED.Id
+            VALUES (@ChargeBoxId, @ConnectorId, @CreatedOn, @UpdatedOn, @StartTransactionReportedOn, @StopTransactionReportedOn,
+            @StartIdTag, @MeterStart, @MeterStop, @CustomerId, @StartTime, @StopTime, @ReservationId, @RetryStartTransactionTimes, @RetryStopTransactionTimes, @Fee)
+            """, parameters);
+        return id;
+    }
+
+    private async Task<TransactionRecord> GetTransactionForStopTransactionEF(int transactionId, string chargeBoxId)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        return await db.TransactionRecord.Where(x => x.Id == transactionId
+             && x.ChargeBoxId == chargeBoxId).FirstOrDefaultAsync();
+    }
+
+    private async Task<TransactionRecord> GetTransactionForStopTransactionDapper(int transactionId, string chargeBoxId)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@TransactionId", transactionId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var record = await conn.QuerySingleAsync<TransactionRecord>("""
+            SELECT ConnectorId, MeterStop, MeterStart, StopTime FROM TransactionRecord
+            WHERE Id = @TransactionId and ChargeBoxId = @ChargeBoxId 
+            """, parameters);
+        return record;
     }
 
     private Task BulkInsertServerMessage(IEnumerable<ServerMessage> messages)
@@ -707,6 +832,76 @@ public class MainDbService : IMainDbService
         }
         return limit;
     }
+
+    private async Task<bool> UpdateHeartBeatsDapper(IEnumerable<Machine> heartBeatsData)
+    {
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        using var trans = await conn.BeginTransactionAsync();
+
+        try
+        {
+            foreach (var data in heartBeatsData)
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@Id", data.Id, DbType.String, ParameterDirection.Input, 36);
+                parameters.Add("@HeartbeatUpdatedOn", data.HeartbeatUpdatedOn, DbType.DateTime, ParameterDirection.Input);
+                parameters.Add("@ConnectionType", data.ConnectionType, DbType.Int32, ParameterDirection.Input);
+
+                var resultCnt = await conn.ExecuteAsync("""
+                    UPDATE Machine
+                    SET HeartbeatUpdatedOn = @HeartbeatUpdatedOn, ConnectionType = @ConnectionType
+                    WHERE Id = @Id
+                    """, parameters, trans);
+                if (resultCnt != 1)
+                {
+                    throw new Exception("Update over one columes");
+                }
+            }
+            await trans.CommitAsync();
+        }
+        catch
+        {
+            logger.LogCritical("HeartBeatCheckTrigger update fail, roll back");
+            await trans.RollbackAsync();
+            return false;
+        }
+
+        return true;
+    }
+
+    private async Task<bool> UpdateHeartBeatsEF(IEnumerable<Machine> heartBeatsData)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        using var transaction = await db.Database.BeginTransactionAsync();
+
+        try
+        {
+            foreach (var data in heartBeatsData)
+            {
+                var machine = new Machine() { Id = data.Id };
+                if (machine != null)
+                {
+                    db.Machine.Attach(machine);
+                    machine.HeartbeatUpdatedOn = DateTime.UtcNow;
+                    machine.ConnectionType = data.ConnectionType;
+                    db.Entry(machine).Property(x => x.HeartbeatUpdatedOn).IsModified = true;
+                    db.Entry(machine).Property(x => x.ConnectionType).IsModified = true;
+                }
+            }
+
+            await db.SaveChangesAsync();
+            await transaction.CommitAsync();
+            db.ChangeTracker.Clear();
+        }
+        catch (Exception ex)
+        {
+            logger.LogCritical(ex, "HeartBeatCheckTrigger update fail, roll back");
+            transaction.Rollback();
+            return false;
+        }
+
+        return true;
+    }
 }
 
 public record MachineAndCustomerInfo (string MachineId, Guid CustomerId, string CustomerName);

+ 73 - 77
EVCB_OCPP.WSServer/Service/MeterValueDbService.cs

@@ -1,20 +1,14 @@
 using Dapper;
 using EVCB_OCPP.Domain;
-using EVCB_OCPP.Packet.Messages.SubTypes;
 using EVCB_OCPP.WSServer.Helper;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Configuration;
 using System.Data;
+using System.Data.Common;
 using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace EVCB_OCPP.WSServer.Service;
 
@@ -25,7 +19,6 @@ public class MeterValueDbService
     private readonly ILoggerFactory loggerFactory;
     private readonly MeterValueGroupSingleHandler meterValueGroupSingleHandler;
     private readonly QueueSemaphore insertSemaphore;
-    //private readonly string meterValueConnectionString;
     private readonly ILogger logger;
     private readonly Queue<string> _existTables = new();
     private GroupSingleHandler<InsertMeterValueParam> insertMeterValueHandler;
@@ -57,34 +50,24 @@ public class MeterValueDbService
             , int contextId, int formatId, int measurandId, int phaseId
             , int locationId, int unitId, int transactionId)
     {
-        //using var token = await insertSemaphore.GetToken();
-        //using var db = await meterValueDbContextFactory.CreateDbContextAsyncAsync();
-
-        //string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId, @ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
-
         var param = new InsertMeterValueParam(chargeBoxId, connectorId, value, createdOn, contextId, formatId, measurandId, phaseId, locationId, unitId, transactionId);
 
-        //List<SqlParameter> parameter = new List<SqlParameter>();
-        //parameter.AddInsertMeterValueRecordSqlParameters(
-        //    chargeBoxId: param.chargeBoxId
-        //    , connectorId: (byte)param.connectorId
-        //    , value: param.value
-        //    , createdOn: param.createdOn
-        //    , contextId: param.contextId
-        //    , formatId: param.formatId
-        //    , measurandId: param.measurandId
-        //    , phaseId: param.phaseId
-        //    , locationId: param.locationId
-        //    , unitId: param.unitId
-        //    , transactionId: param.transactionId);
-
-        //await db.Database.ExecuteSqlRawAsync(sp, parameter.ToArray());
-
         //return insertMeterValueHandler.HandleAsync(param);
-        return InsertWithDapper(param);
+        return InsertAsync(param);
         //return meterValueGroupSingleHandler.HandleAsync(param);
     }
 
+    public Task InsertAsync(InsertMeterValueParam param)
+    {
+        //return InsertWithEF(param);
+        return InsertWithDapper(param);
+    }
+
+    public Task InsertBundleAsync(IEnumerable<InsertMeterValueParam> param)
+    {
+        return BundleInsertWithDapper(param);
+    }
+
     private void InitInsertMeterValueHandler()
     {
         if (insertMeterValueHandler is not null)
@@ -109,10 +92,35 @@ public class MeterValueDbService
         }
     }
 
+    private async Task InsertWithEF(InsertMeterValueParam param)
+    {
+        using var token = await insertSemaphore.GetToken();
+        using var db = await meterValueDbContextFactory.CreateDbContextAsync();
+
+        string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId, @ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
+
+        List<SqlParameter> parameter = new List<SqlParameter>();
+        parameter.AddInsertMeterValueRecordSqlParameters(
+            chargeBoxId: param.chargeBoxId
+            , connectorId: (byte)param.connectorId
+            , value: param.value
+            , createdOn: param.createdOn
+            , contextId: param.contextId
+            , formatId: param.formatId
+            , measurandId: param.measurandId
+            , phaseId: param.phaseId
+            , locationId: param.locationId
+            , unitId: param.unitId
+            , transactionId: param.transactionId);
+
+        await db.Database.ExecuteSqlRawAsync(sp, parameter.ToArray());
+    }
+
     private async Task InsertWithDapper(InsertMeterValueParam param)
     {
         var watch = Stopwatch.StartNew();
-        long t0, t1, t2, t3;
+        long t0, t1;
+
         if (!await GetTableExist(param.createdOn))
         {
             t0 = watch.ElapsedMilliseconds;
@@ -128,34 +136,14 @@ public class MeterValueDbService
 
         t0 = watch.ElapsedMilliseconds;
         var tableName = GetTableName(param.createdOn);
-        string command = $"""
-            INSERT INTO {tableName} (ConnectorId, Value, CreatedOn, ContextId, FormatId, MeasurandId, PhaseId, LocationId, UnitId, ChargeBoxId, TransactionId)
-            VALUES (@ConnectorId, @Value, @CreatedOn, @ContextId, @FormatId, @MeasurandId, @PhaseId, @LocationId, @UnitId, @ChargeBoxId, @TransactionId);
-            """;
 
-        var parameters = new DynamicParameters();
-        parameters.Add("ConnectorId", param.connectorId, DbType.Int16);
-        parameters.Add("Value", param.value, DbType.Decimal, precision: 18, scale: 8);
-        parameters.Add("CreatedOn", param.createdOn, DbType.DateTime);
-        parameters.Add("ContextId", param.contextId, DbType.Int32);
-        parameters.Add("FormatId", param.formatId, DbType.Int32);
-        parameters.Add("MeasurandId", param.measurandId, DbType.Int32);
-        parameters.Add("PhaseId", param.phaseId, DbType.Int32);
-        parameters.Add("LocationId", param.locationId, DbType.Int32);
-        parameters.Add("UnitId", param.unitId, DbType.Int32);
-        parameters.Add("ChargeBoxId", param.chargeBoxId, DbType.String, size: 50);
-        parameters.Add("TransactionId", param.transactionId, DbType.Int32);
-
-        t1 = watch.ElapsedMilliseconds;
-        using var sqlConnection = await sqlConnectionFactory.CreateAsync();
-        t2 = watch.ElapsedMilliseconds;
-        await sqlConnection.ExecuteAsync(command, parameters).ConfigureAwait(false);
+        await InsertWithNoCheckDapper(tableName, param);
 
         watch.Stop();
-        t3 = watch.ElapsedMilliseconds;
-        if(t3 > 700)
+        t1 = watch.ElapsedMilliseconds;
+        if(t1 > 700)
         {
-            logger.LogWarning("MeterValue Dapper {0}/{1}/{2}/{3}", t0, t1, t2, t3);
+            logger.LogWarning("MeterValue Dapper {0}/{1}", t0, t1);
         }
     }
 
@@ -180,39 +168,22 @@ public class MeterValueDbService
 
         t1 = watch.ElapsedMilliseconds;
         using SqlConnection sqlConnection = await sqlConnectionFactory.CreateAsync();
-        using var tans = await sqlConnection.BeginTransactionAsync();
+        using var trans = await sqlConnection.BeginTransactionAsync();
 
         t2 = watch.ElapsedMilliseconds;
 
         List<Task> ExecuteTasks = new List<Task>();
         foreach (var group in gruopParams)
         {
-
             var tableName = group.Key;
-            string command = $"""
-                INSERT INTO {tableName} (ConnectorId, Value, CreatedOn, ContextId, FormatId, MeasurandId, PhaseId, LocationId, UnitId, ChargeBoxId, TransactionId)
-                VALUES (@ConnectorId, @Value, @CreatedOn, @ContextId, @FormatId, @MeasurandId, @PhaseId, @LocationId, @UnitId, @ChargeBoxId, @TransactionId);
-                """;
             foreach(var param in group)
             {
-                var parameters = new DynamicParameters();
-                parameters.Add("ConnectorId", param.connectorId, DbType.Int16);
-                parameters.Add("Value", param.value, DbType.Decimal,precision:18, scale:8);
-                parameters.Add("CreatedOn", param.createdOn, DbType.DateTime);
-                parameters.Add("ContextId", param.contextId, DbType.Int32);
-                parameters.Add("FormatId", param.formatId, DbType.Int32);
-                parameters.Add("MeasurandId", param.measurandId, DbType.Int32);
-                parameters.Add("PhaseId", param.phaseId, DbType.Int32);
-                parameters.Add("LocationId", param.locationId, DbType.Int32);
-                parameters.Add("UnitId", param.unitId, DbType.Int32);
-                parameters.Add("ChargeBoxId", param.chargeBoxId, DbType.String, size:50);
-                parameters.Add("TransactionId", param.transactionId, DbType.Int32);
-                await sqlConnection.ExecuteAsync(command, parameters, tans);
+                await InsertWithNoCheckDapper(tableName, param, sqlConnection, trans);
             }
         }
 
         t3 = watch.ElapsedMilliseconds;
-        await tans.CommitAsync();
+        await trans.CommitAsync();
 
         watch.Stop();
         t4 = watch.ElapsedMilliseconds;
@@ -222,6 +193,31 @@ public class MeterValueDbService
         }
     }
 
+    private async Task InsertWithNoCheckDapper(string tableName, InsertMeterValueParam data, SqlConnection conn = null, DbTransaction trans = null)
+    {
+        string command = $"""
+                INSERT INTO {tableName} (ConnectorId, Value, CreatedOn, ContextId, FormatId, MeasurandId, PhaseId, LocationId, UnitId, ChargeBoxId, TransactionId)
+                VALUES (@ConnectorId, @Value, @CreatedOn, @ContextId, @FormatId, @MeasurandId, @PhaseId, @LocationId, @UnitId, @ChargeBoxId, @TransactionId);
+                """;
+
+        SqlConnection connection = conn ?? await sqlConnectionFactory.CreateAsync();
+
+        var parameters = new DynamicParameters();
+        parameters.Add("ConnectorId", data.connectorId, DbType.Int16);
+        parameters.Add("Value", data.value, DbType.Decimal, precision: 18, scale: 8);
+        parameters.Add("CreatedOn", data.createdOn, DbType.DateTime);
+        parameters.Add("ContextId", data.contextId, DbType.Int32);
+        parameters.Add("FormatId", data.formatId, DbType.Int32);
+        parameters.Add("MeasurandId", data.measurandId, DbType.Int32);
+        parameters.Add("PhaseId", data.phaseId, DbType.Int32);
+        parameters.Add("LocationId", data.locationId, DbType.Int32);
+        parameters.Add("UnitId", data.unitId, DbType.Int32);
+        parameters.Add("ChargeBoxId", data.chargeBoxId, DbType.String, size: 50);
+        parameters.Add("TransactionId", data.transactionId, DbType.Int32);
+
+        await connection.ExecuteAsync(command, parameters, trans);
+    }
+
     private async Task BulkInsertWithBulkCopy(IEnumerable<InsertMeterValueParam> parms)
     {
         var watcher = Stopwatch.StartNew();
@@ -350,7 +346,7 @@ public class MeterValueDbService
             , unitId: param.unitId
             , transactionId: param.transactionId);
 
-        db.Database.ExecuteSqlRaw(sp, parameter.ToArray());
+        await db.Database.ExecuteSqlRawAsync(sp, parameter.ToArray());
     }
 
     private static string GetTableName(DateTime dateTime) 

+ 12 - 0
EVCB_OCPP.WSServer/appsettings.json

@@ -103,6 +103,18 @@
         "logger": "Microsoft.*",
         "minlevel": "Warn",
         "writeTo": "console"
+      },
+      {
+        "ruleName": "DbConsoleLog",
+        "logger": "System.Data.Entity.*",
+        "minlevel": "Info",
+        "writeTo": "console"
+      },
+      {
+        "ruleName": "DbFileLog",
+        "logger": "System.Data.Entity.*",
+        "minlevel": "Info",
+        "writeTo": "f"
       }
     ]
   },

+ 2 - 0
sysctl.conf

@@ -0,0 +1,2 @@
+net.core.wmem_max=12582912
+net.core.rmem_max=12582912