Example #1
0
        private object InvokeDeserializeJsonToExistingData(Type classType, JObject jObject, object data, object localId, object transaction, OperationType operationType, ConflictType conflictType, string synchronizationId, Dictionary <string, object> customInfo, SyncConfiguration.SchemaInfo localSchemaInfo)
        {
            object existingData = DeserializeJsonToExistingData(classType, jObject, data, transaction, operationType, conflictType, synchronizationId, customInfo);

            if (conflictType != ConflictType.NoConflict && existingData == null)
            {
                return(null);
            }
            if (existingData == null)
            {
                throw new SyncEngineConstraintException($"{nameof(DeserializeJsonToExistingData)} must not return null for {nameof(conflictType)} equals to {conflictType.ToString()}");
            }
            if (existingData.GetType().FullName != classType.FullName)
            {
                throw new SyncEngineConstraintException($"Expected returned Type: {classType.FullName} during {nameof(DeserializeJsonToExistingData)}, but Type: {existingData.GetType().FullName} is returned instead.");
            }
            object existingDataId = classType.GetProperty(localSchemaInfo.PropertyInfoId.Name).GetValue(existingData);

            if (!existingDataId.Equals(localId))
            {
                throw new SyncEngineConstraintException($"The returned Object Id ({existingDataId}) is different than the existing data Id: {localId}");
            }
            return(existingData);
        }
Example #2
0
        internal (Type localSyncType, List <object> appliedIds, List <object> deletedIds) ApplyTypeChanges(List <string> log, List <SyncLog.SyncLogData> inserts, List <SyncLog.SyncLogData> updates, List <SyncLog.SyncLogData> deletes, List <SyncLog.SyncLogConflict> conflicts, JObject typeChanges, string synchronizationId, Dictionary <string, object> customInfo, string sourceDatabaseInstanceId, string destinationDatabaseInstanceId)
        {
            if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
            {
                if (!string.IsNullOrEmpty(sourceDatabaseInstanceId) || !string.IsNullOrEmpty(destinationDatabaseInstanceId))
                {
                    throw new Exception($"{SyncConfiguration.TimeStampStrategy.ToString()} must have {nameof(sourceDatabaseInstanceId)} and {nameof(destinationDatabaseInstanceId)} equals to null");
                }
            }
            else if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
            {
                if (string.IsNullOrEmpty(sourceDatabaseInstanceId) || string.IsNullOrEmpty(destinationDatabaseInstanceId))
                {
                    throw new Exception($"{SyncConfiguration.TimeStampStrategy.ToString()} must have {nameof(sourceDatabaseInstanceId)} and {nameof(destinationDatabaseInstanceId)} both not null");
                }
            }
            else
            {
                throw new NotImplementedException(SyncConfiguration.TimeStampStrategy.ToString());
            }

            List <object>             appliedIds = new List <object>();
            List <object>             deletedIds = new List <object>();
            Dictionary <string, long> databaseInstanceMaxTimeStamps = new Dictionary <string, long>();

            if (inserts == null)
            {
                inserts = new List <SyncLog.SyncLogData>();
            }
            if (updates == null)
            {
                updates = new List <SyncLog.SyncLogData>();
            }
            if (deletes == null)
            {
                deletes = new List <SyncLog.SyncLogData>();
            }
            if (conflicts == null)
            {
                conflicts = new List <SyncLog.SyncLogConflict>();
            }
            string  syncTypeName      = typeChanges["syncType"].Value <string>();
            JObject jObjectSchemaInfo = typeChanges["schemaInfo"].Value <JObject>();

            SyncConfiguration.SchemaInfo schemaInfo = SyncConfiguration.SchemaInfo.FromJObject(jObjectSchemaInfo);
            string localSyncTypeName = syncTypeName;

            if (!string.IsNullOrEmpty(schemaInfo.SyncSchemaAttribute.MapToClassName))
            {
                localSyncTypeName = schemaInfo.SyncSchemaAttribute.MapToClassName;
            }
            Type localSyncType = SyncConfiguration.SyncTypes.Where(w => w.Name == localSyncTypeName).FirstOrDefault();

            if (localSyncType == null)
            {
                throw new SyncEngineConstraintException($"Unable to find SyncType: {localSyncTypeName} in SyncConfiguration");
            }
            SyncConfiguration.SchemaInfo localSchemaInfo = GetSchemaInfo(SyncConfiguration, localSyncType);

            OperationType operationType = OperationType.ApplyChanges;
            object        transaction   = StartTransaction(localSyncType, operationType, synchronizationId, customInfo);

            try
            {
                IQueryable queryable = InvokeGetQueryable(localSyncType, transaction, operationType, synchronizationId, customInfo);
                JArray     datas     = typeChanges["datas"].Value <JArray>();
                log.Add($"Data Count: {datas.Count}");
                for (int i = 0; i < datas.Count; i++)
                {
                    JObject jObjectData = datas[i].Value <JObject>();
                    JValue  id          = jObjectData[schemaInfo.PropertyInfoId.Name].Value <JValue>();
                    long    lastUpdated = jObjectData[schemaInfo.PropertyInfoLastUpdated.Name].Value <long>();

                    long?  deletedGlobalTimeStamp   = null;
                    bool   deletedDatabaseTimeStamp = false;
                    string databaseInstanceId       = null;
                    if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
                    {
                        deletedGlobalTimeStamp = jObjectData[schemaInfo.PropertyInfoDeleted.Name].Value <long?>();
                    }
                    if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                    {
                        deletedDatabaseTimeStamp = jObjectData[schemaInfo.PropertyInfoDeleted.Name].Value <bool>();
                        databaseInstanceId       = jObjectData[schemaInfo.PropertyInfoDatabaseInstanceId.Name].Value <string>();
                    }

                    object  localId     = TransformIdType(localSyncType, id, transaction, operationType, synchronizationId, customInfo);
                    dynamic dynamicData = queryable.Where($"{localSchemaInfo.PropertyInfoId.Name} == @0", localId).FirstOrDefault();
                    object  localData   = (object)dynamicData;

                    if (localData == null)
                    {
                        object newData = InvokeDeserializeJsonToNewData(localSyncType, jObjectData, transaction, operationType, synchronizationId, customInfo);
                        newData.GetType().GetProperty(localSchemaInfo.PropertyInfoId.Name).SetValue(newData, localId);
                        newData.GetType().GetProperty(localSchemaInfo.PropertyInfoLastUpdated.Name).SetValue(newData, lastUpdated);

                        if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
                        {
                            newData.GetType().GetProperty(localSchemaInfo.PropertyInfoDeleted.Name).SetValue(newData, deletedGlobalTimeStamp);
                        }
                        if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                        {
                            newData.GetType().GetProperty(localSchemaInfo.PropertyInfoDeleted.Name).SetValue(newData, deletedDatabaseTimeStamp);
                            newData.GetType().GetProperty(localSchemaInfo.PropertyInfoDatabaseInstanceId.Name).SetValue(newData, GetCorrectDatabaseInstanceId(databaseInstanceId, sourceDatabaseInstanceId, destinationDatabaseInstanceId, databaseInstanceMaxTimeStamps, lastUpdated));
                        }

                        PersistData(localSyncType, newData, true, transaction, operationType, synchronizationId, customInfo);
                        if (!appliedIds.Contains(localId))
                        {
                            appliedIds.Add(localId);
                        }
                        inserts.Add(SyncLog.SyncLogData.FromJObject(InvokeSerializeDataToJson(localSyncType, newData, localSchemaInfo, transaction, operationType, synchronizationId, customInfo), localSyncType, localSchemaInfo));
                    }
                    else
                    {
                        bool isDeleted = false;
                        if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
                        {
                            if (deletedGlobalTimeStamp != null)
                            {
                                isDeleted = true;
                            }
                        }
                        if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                        {
                            if (deletedDatabaseTimeStamp)
                            {
                                isDeleted = true;
                            }
                        }

                        if (!isDeleted)
                        {
                            ConflictType updateConflictType = ConflictType.NoConflict;
                            long         localLastUpdated   = (long)localData.GetType().GetProperty(localSchemaInfo.PropertyInfoLastUpdated.Name).GetValue(localData);
                            if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
                            {
                                if (localLastUpdated > lastUpdated)
                                {
                                    updateConflictType = ConflictType.ExistingDataIsNewerThanIncomingData;
                                }
                            }
                            if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                            {
                                string localDatabaseInstanceId   = (string)localData.GetType().GetProperty(localSchemaInfo.PropertyInfoDatabaseInstanceId.Name).GetValue(localData);
                                string correctDatabaseInstanceId = GetCorrectDatabaseInstanceId(databaseInstanceId, sourceDatabaseInstanceId, destinationDatabaseInstanceId, null, 0);
                                if (localDatabaseInstanceId == correctDatabaseInstanceId)
                                {
                                    if (localLastUpdated > lastUpdated)
                                    {
                                        updateConflictType = ConflictType.ExistingDataIsNewerThanIncomingData;
                                    }
                                }
                                else
                                {
                                    updateConflictType = ConflictType.ExistingDataIsUpdatedByDifferentDatabaseInstanceId;
                                }
                            }

                            object existingData = InvokeDeserializeJsonToExistingData(localSyncType, jObjectData, localData, localId, transaction, operationType, updateConflictType, synchronizationId, customInfo, localSchemaInfo);
                            if (existingData == null && updateConflictType == ConflictType.NoConflict)
                            {
                                throw new SyncEngineConstraintException($"{nameof(DeserializeJsonToExistingData)} must not return null for conflictType equals to {ConflictType.NoConflict.ToString()}");
                            }
                            if (existingData != null)
                            {
                                existingData.GetType().GetProperty(localSchemaInfo.PropertyInfoLastUpdated.Name).SetValue(existingData, lastUpdated);
                                if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                                {
                                    existingData.GetType().GetProperty(localSchemaInfo.PropertyInfoDatabaseInstanceId.Name).SetValue(existingData, GetCorrectDatabaseInstanceId(databaseInstanceId, sourceDatabaseInstanceId, destinationDatabaseInstanceId, databaseInstanceMaxTimeStamps, lastUpdated));
                                }
                                PersistData(localSyncType, existingData, false, transaction, operationType, synchronizationId, customInfo);
                                if (!appliedIds.Contains(localId))
                                {
                                    appliedIds.Add(localId);
                                }
                                updates.Add(SyncLog.SyncLogData.FromJObject(InvokeSerializeDataToJson(localSyncType, existingData, localSchemaInfo, transaction, operationType, synchronizationId, customInfo), localSyncType, localSchemaInfo));
                            }
                            else
                            {
                                log.Add($"CONFLICT Detected: {updateConflictType.ToString()}. Id: {id}");
                                conflicts.Add(new SyncLog.SyncLogConflict(updateConflictType, SyncLog.SyncLogData.FromJObject(jObjectData, localSyncType, schemaInfo)));
                            }
                        }
                        else
                        {
                            object existingData = InvokeDeserializeJsonToExistingData(localSyncType, jObjectData, localData, localId, transaction, operationType, ConflictType.NoConflict, synchronizationId, customInfo, localSchemaInfo);

                            if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.GlobalTimeStamp)
                            {
                                existingData.GetType().GetProperty(localSchemaInfo.PropertyInfoDeleted.Name).SetValue(existingData, deletedGlobalTimeStamp);
                            }
                            if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                            {
                                existingData.GetType().GetProperty(localSchemaInfo.PropertyInfoDeleted.Name).SetValue(existingData, deletedDatabaseTimeStamp);
                                existingData.GetType().GetProperty(localSchemaInfo.PropertyInfoDatabaseInstanceId.Name).SetValue(existingData, GetCorrectDatabaseInstanceId(databaseInstanceId, sourceDatabaseInstanceId, destinationDatabaseInstanceId, databaseInstanceMaxTimeStamps, lastUpdated));
                            }

                            PersistData(localSyncType, existingData, false, transaction, operationType, synchronizationId, customInfo);
                            if (!appliedIds.Contains(localId))
                            {
                                appliedIds.Add(localId);
                            }
                            if (!deletedIds.Contains(localId))
                            {
                                deletedIds.Add(localId);
                            }
                            deletes.Add(SyncLog.SyncLogData.FromJObject(InvokeSerializeDataToJson(localSyncType, existingData, localSchemaInfo, transaction, operationType, synchronizationId, customInfo), localSyncType, localSchemaInfo));
                        }
                    }
                }
                if (SyncConfiguration.TimeStampStrategy == SyncConfiguration.TimeStampStrategyEnum.DatabaseTimeStamp)
                {
                    foreach (var item in databaseInstanceMaxTimeStamps)
                    {
                        KnowledgeInfo knowledgeInfo = GetAllKnowledgeInfos(synchronizationId, customInfo).Where(w => w.DatabaseInstanceId == item.Key).FirstOrDefault();
                        if (knowledgeInfo != null && knowledgeInfo.MaxTimeStamp > item.Value)
                        {
                            continue;
                        }
                        if (knowledgeInfo == null && item.Key == destinationDatabaseInstanceId)
                        {
                            throw new SyncEngineConstraintException("Unexpected Knowledge Info State on Destination. Destination is not provisioned yet.");
                        }
                        if (knowledgeInfo == null)
                        {
                            knowledgeInfo = new KnowledgeInfo()
                            {
                                DatabaseInstanceId = item.Key,
                                IsLocal            = false
                            };
                        }
                        knowledgeInfo.MaxTimeStamp = item.Value;
                        CreateOrUpdateKnowledgeInfo(knowledgeInfo, synchronizationId, customInfo);
                    }
                }
                CommitTransaction(localSyncType, transaction, operationType, synchronizationId, customInfo);
            }
            catch (Exception)
            {
                RollbackTransaction(localSyncType, transaction, operationType, synchronizationId, customInfo);
                throw;
            }
            finally
            {
                EndTransaction(localSyncType, transaction, operationType, synchronizationId, customInfo);
            }

            return(localSyncType, appliedIds, deletedIds);
        }