/// <summary> /// Copies unprocessed messages from local database to remote. /// If copied successfully, source's property IsProcessed = true. /// </summary> /// <param name="ct"></param> /// <returns></returns> public async Task CopyNewLocalMessagesToRemote(CancellationToken ct = default) { var localMessages = _localContext.Messages.Where(x => x.IsProcessed == false).ToList(); if (localMessages.Count == 0) { return; } // Clone messages var clonedMessages = new List <Message>(); localMessages.ForEach(m => clonedMessages.Add(m.Clone())); // Add cloned messages to remote context and save. // Because local database does not support column type of C# DateTimeOffset, // before insert to Azure MSSql, CreatedOn is retrieved from MessageBody field. clonedMessages.ForEach(x => { x.Id = 0; var messageBody = JsonConvert.DeserializeAnonymousType(x.MessageBody, new { CreatedOn = "" }); x.CreatedOn = DateTime.Parse(messageBody.CreatedOn); }); await _remoteContext.Messages.AddRangeAsync(clonedMessages, ct); //remoteContext.Messages.AddRange(clonedMessages); var savedClonedMessagesCount = 0; try { savedClonedMessagesCount = await _remoteContext.SaveChangesAsync(ct); } catch (Exception e) { _logger.LogError(e, "Database error"); throw; } //var savedClonedMessagesCount = remoteContext.SaveChanges(); // If copied succesfully, mark local as processed. if (savedClonedMessagesCount == clonedMessages.Count) { localMessages.ForEach(x => x.IsProcessed = true); _localContext.UpdateRange(localMessages); try { await _localContext.SaveChangesAsync(ct); } catch (Exception e) { _logger.LogError(e, "Database error while copying messages to remote."); } } _logger.LogDebug("New Message records added to Azure: {savedClonedMessagesCount}", savedClonedMessagesCount); return; }