/// <summary> /// Publish source points /// Download the word file and update the destination point value with source point value in word file then upload the word file to overwrite existed one. /// /// </summary> /// <param name="publishSourcePointForms"></param> /// <returns></returns> public async Task <PublishSourcePointResult> PublishSourcePointListAsync(IEnumerable <PublishSourcePointForm> publishSourcePointForms) { try { var sourcePointIdList = publishSourcePointForms.Select(o => o.SourcePointId).ToArray(); var sourcePointList = _dbContext.SourcePoints.Include(o => o.Catalog).Include(o => o.PublishedHistories).Where(o => sourcePointIdList.Contains(o.Id)).ToList(); var currentUser = _userProfileService.GetCurrentUser(); //Update database IList <PublishedHistory> histories = new List <PublishedHistory>(); foreach (var sourcePoint in sourcePointList) { sourcePoint.Value = publishSourcePointForms.First(o => o.SourcePointId == sourcePoint.Id).CurrentValue; sourcePoint.Position = publishSourcePointForms.First(o => o.SourcePointId == sourcePoint.Id).Position; sourcePoint.Name = publishSourcePointForms.First(o => o.SourcePointId == sourcePoint.Id).Name; sourcePoint.NamePosition = publishSourcePointForms.First(o => o.SourcePointId == sourcePoint.Id).NamePosition; var history = _mapper.Map <PublishedHistory>(sourcePoint); history.PublishedDate = DateTime.Now.ToUniversalTime().ToPSTDateTime(); history.PublishedUser = currentUser.Username; sourcePoint.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 separate 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.WriteMessageToQueueAsync(JsonConvert.SerializeObject(message), Constant.PUBLISH_QUEUE_NAME); await _logService.WriteLogAsync(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); foreach (var item in sourcePointList) { item.PublishedHistories = item.PublishedHistories.OrderByDescending(p => p.PublishedDate).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.WriteLogAsync(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.WriteLogAsync(logEntity); throw ex; } }