public async Task <PublishSourcePointResult> PublishSourcePointList(IEnumerable <PublishSourcePointForm> publishSourcePointForms)
        {
            try
            {
                var sourcePointIdList = publishSourcePointForms.Select(o => o.SourcePointId).ToArray();
                var sourcePointList   = _dbContext.SourcePoints.Include(o => o.Catalog).Where(o => sourcePointIdList.Contains(o.Id)).ToList();
                var currentUser       = _userProfileService.GetCurrentUser();

                //Update database
                IList <PublishedHistory> histories = new List <PublishedHistory>();
                foreach (var sourcePoint in sourcePointList)
                {
                    var sourcePointForm = publishSourcePointForms.First(o => o.SourcePointId == sourcePoint.Id);
                    sourcePoint.Value        = sourcePointForm.CurrentValue;
                    sourcePoint.Position     = sourcePointForm.Position;
                    sourcePoint.Name         = sourcePointForm.Name;
                    sourcePoint.NamePosition = sourcePointForm.NamePosition;

                    var history = new PublishedHistory()
                    {
                        Name          = sourcePoint.Name,
                        Position      = sourcePoint.Position,
                        Value         = sourcePoint.Value,
                        PublishedDate = DateTime.Now.ToUniversalTime().ToPSTDateTime(),
                        PublishedUser = currentUser.Username,
                        SourcePointId = sourcePoint.Id
                    };

                    _dbContext.PublishedHistories.Add(history);

                    histories.Add(history);
                }
                await _dbContext.SaveChangesAsync();

                //Update Table Storage
                var table = _azureStorageService.GetTable(Constant.PUBLISH_TABLE_NAME);

                var batchId = Guid.NewGuid();

                //A single batch operation can include up to 100 entities, so seperate histories into several batch when histories
                //https://docs.microsoft.com/en-us/azure/storage/storage-dotnet-how-to-use-tables
                var batchCount = Math.Ceiling((float)histories.Count() / Constant.AZURETABLE_BATCH_COUNT);
                var batchTasks = new List <Task <IList <TableResult> > >();
                for (int i = 0; i < batchCount; i++)
                {
                    var historiesPerBatch = histories.Skip(Constant.AZURETABLE_BATCH_COUNT * i).Take(Constant.AZURETABLE_BATCH_COUNT);
                    var batchOpt          = new TableBatchOperation();
                    foreach (var item in historiesPerBatch)
                    {
                        batchOpt.Insert(new PublishStatusEntity(batchId.ToString(), item.SourcePointId.ToString(), item.Id.ToString()));
                    }
                    batchTasks.Add(table.ExecuteBatchAsync(batchOpt));
                }

                Task.WaitAll(batchTasks.ToArray());
                IList <SourcePoint> errorSourcePoints = new List <SourcePoint>();
                var batchResults = batchTasks.SelectMany(o => o.Result).ToArray();
                for (int i = 0; i < batchResults.Count(); i++)
                {
                    if (!(batchResults[i].HttpStatusCode == 200 || batchResults[i].HttpStatusCode == 204))
                    {
                        errorSourcePoints.Add(sourcePointList[i]);
                    }
                }
                if (errorSourcePoints.Count() > 0)
                {
                    throw new SourcePointException(sourcePointList, string.Concat(batchResults.Where(o => !(o.HttpStatusCode == 200 || o.HttpStatusCode == 204)).Select(o => o.Result)), null);
                }

                //Push message to queue
                IList <Task> writeQueueTask = new List <Task>();
                foreach (var history in histories)
                {
                    var message = new PublishedMessage()
                    {
                        PublishHistoryId = history.Id, SourcePointId = history.SourcePointId, PublishBatchId = batchId
                    };
                    writeQueueTask.Add(Task.Run(async() =>
                    {
                        await _azureStorageService.WriteMessageToQueue(JsonConvert.SerializeObject(message), Constant.PUBLISH_QUEUE_NAME);
                        await _logService.WriteLog(new LogEntity()
                        {
                            LogId      = "10004",
                            Action     = Constant.ACTIONTYPE_PUBLISH,
                            ActionType = ActionTypeEnum.AuditLog,
                            PointType  = Constant.POINTTYPE_SOURCEPOINT,
                            Message    = $"Publish source point named: {history.Name} in the location {history.Position}, value: {history.Value} in the excel file named:{history.SourcePoint.Catalog.Name} by {currentUser.Username}"
                        });
                    }));
                }
                await Task.WhenAll(writeQueueTask);

                //var publishedHistories = await (from pb in _dbContext.PublishedHistories
                //                                where sourcePointIdList.Contains(pb.SourcePointId)
                //                                select pb).ToArrayAsync();
                foreach (var item in sourcePointList)
                {
                    item.PublishedHistories = histories.Where(h => h.SourcePointId == item.Id).ToArray();
                }

                return(new PublishSourcePointResult()
                {
                    BatchId = batchId, SourcePoints = sourcePointList
                });
            }
            catch (SourcePointException ex)
            {
                foreach (var sourcePoint in ex.ErrorSourcePoints)
                {
                    var logEntity = new LogEntity()
                    {
                        LogId      = "10007",
                        Action     = Constant.ACTIONTYPE_PUBLISH,
                        ActionType = ActionTypeEnum.ErrorLog,
                        PointType  = Constant.POINTTYPE_SOURCEPOINT,
                        Message    = ".Net Error",
                        Detail     = $"{sourcePoint.Id}-{sourcePoint.Name}-{ex.ToString()}"
                    };
                    logEntity.Subject = $"{logEntity.LogId} - {logEntity.Action} - {logEntity.PointType} - Error";
                    await _logService.WriteLog(logEntity);
                }

                throw ex;
            }
            catch (Exception ex)
            {
                var logEntity = new LogEntity()
                {
                    LogId      = "10007",
                    Action     = Constant.ACTIONTYPE_PUBLISH,
                    ActionType = ActionTypeEnum.ErrorLog,
                    PointType  = Constant.POINTTYPE_SOURCEPOINT,
                    Message    = ".Net Error",
                    Detail     = ex.ToString()
                };
                logEntity.Subject = $"{logEntity.LogId} - {logEntity.Action} - {logEntity.PointType} - Error";
                await _logService.WriteLog(logEntity);

                throw ex;
            }
        }