async Task <int> SyncDataAsync(PartitionContext context, List <EventData> messages)
        {
            for (int i = 0; i < messages.Count; i++)
            {
                EventData       eventData     = messages[i];
                SyncCommandBase syncCommand   = null;
                string          hubName       = (string)eventData.Properties["hubName"];
                string          deviceId      = (string)eventData.Properties["deviceId"];
                string          messageSource = (string)eventData.SystemProperties["iothub-message-source"];
                string          operationType = (string)eventData.Properties["opType"];
                JToken          jPayload      = JToken.Parse(Encoding.UTF8.GetString(eventData.GetBytes()));

                Console.WriteLine();
                Console.WriteLine("=========================================================================================================================================");
                Console.WriteLine("PROCESS EVENT");
                Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------------");
                Console.WriteLine($"Partition: {context.Lease.PartitionId}, Offset: {eventData.Offset}, HubName: {hubName}, DeviceId: {deviceId}, MessageSource: {messageSource} ...");
                Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------------");
                Console.WriteLine($"Operation type '{operationType}'");
                Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------------");
                Console.WriteLine("Body:");
                Console.WriteLine(jPayload.ToString(Newtonsoft.Json.Formatting.Indented));
                Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------------");

                switch (messageSource)
                {
                case "deviceLifecycleEvents":
                    syncCommand = this.GetDeviceLifecycleSyncCommand(eventData, operationType, jPayload, hubName, deviceId);
                    break;

                case "twinChangeEvents":
                    syncCommand = this.GetTwinChangeSyncCommand(eventData, operationType, jPayload, hubName, deviceId);
                    break;

                default:
                    Console.WriteLine($"Message source '{messageSource}' not supported");
                    break;
                }

                if (syncCommand != null)
                {
                    try
                    {
                        await syncCommand.RunAsync();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"WARNING! Failed to sync message with offset {eventData.Offset}");
                        Console.WriteLine(ex.ToString());

                        return(i - 1);
                    }
                }

                Console.WriteLine("=========================================================================================================================================");
            }

            return(messages.Count - 1);
        }
        SyncCommandBase GetDeviceLifecycleSyncCommand(EventData eventData, string operationType, JToken jPayload, string hubName, string deviceId)
        {
            SyncCommandBase syncCommand = null;

            switch (operationType)
            {
            case "createDeviceIdentity":
                syncCommand = this.syncCommandFactory.CreateDeviceIdentitySyncCommand(hubName, deviceId, jPayload);
                break;

            case "deleteDeviceIdentity":
                syncCommand = this.syncCommandFactory.DeleteDeviceIdentitySyncCommand(hubName, deviceId, jPayload);
                break;

            default:
                Console.WriteLine($"Operation type '{operationType}' not supported");
                break;
            }

            return(syncCommand);
        }
        SyncCommandBase GetTwinChangeSyncCommand(EventData eventData, string operationType, JToken jPayload, string hubName, string deviceId)
        {
            SyncCommandBase syncCommand = null;

            switch (operationType)
            {
            case "updateTwin":     // this is a patch
                syncCommand = this.syncCommandFactory.UpdateTwinSyncCommand(hubName, deviceId, jPayload);
                break;

            case "replaceTwin":     // this contains full twin state
                syncCommand = this.syncCommandFactory.ReplaceTwinSyncCommand(hubName, deviceId, jPayload);
                break;

            default:
                Console.WriteLine($"Operation type '{operationType}' not supported");
                break;
            }

            return(syncCommand);
        }