// Asynchronous called method private void Execute(NorthwindConfig config, Digest targetDigest) { #region Declaration SdataContext sdataContext; SupportedResourceKinds resource; IAppBookmarkInfoStore appBookmarkInfoStore; ICorrelatedResSyncInfoStore correlatedResSyncInfoStore; ISyncSyncDigestInfoStore syncDigestStore; ISynctickProvider tickProvider; string resourceKind; string EndPoint; int nexttick = 1; Token lastToken; Token nextToken; Identity[] changedIdentites; IFeedEntryEntityWrapper wrapper; #endregion #region init sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); EndPoint = _parentPerformer._requestContext.DatasetLink + resourceKind; appBookmarkInfoStore = NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext); correlatedResSyncInfoStore = NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext); syncDigestStore = NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); tickProvider = NorthwindAdapter.StoreLocator.GettickProvider(sdataContext); wrapper = FeedEntryWrapperFactory.Create(resource, _parentPerformer._requestContext); #endregion #region get last token or create a new one if (!appBookmarkInfoStore.Get <Token>(resourceKind, out lastToken)) { lastToken = new Token(); lastToken.InitRequest = true; } #endregion #region Get local identities of changed entries since last synchronisation changedIdentites = wrapper.Entity.GetLastChanges(lastToken, config, out nextToken); #endregion if (resource == SupportedResourceKinds.phoneNumbers) { #region workaround for phones for (int index = 0; index < changedIdentites.Length; index++) { string phoneid = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix; string faxId = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix; // receive the resource payloads for the phone id and for the fax id // so that we can calculate their current etags. FeedEntry phoneResourcePayloadContainer = wrapper.GetFeedEntry(phoneid); FeedEntry faxResourcePayloadConatainer = wrapper.GetFeedEntry(faxId); string etag; CorrelatedResSyncInfo[] correlatedResSyncInfos; #region Phone if (phoneResourcePayloadContainer != null) { // calculate etag of the resource etag = EtagServices.ComputeEtag(phoneResourcePayloadContainer, true); // new etag // retrieve correlations for the current identity from synch storage correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { phoneid }); // if no correlation exists AND resource has been deleted: // -> continue with next id // else if no correlation exists: // -> get next tick, create new correlation and add correlation to synch store // otherwise and if the etag stored in synch store is different than etag previously calculated: // -> get next tick, modify existing correlation and update it in synch store. if (correlatedResSyncInfos.Length == 0 && phoneResourcePayloadContainer.IsDeleted) { continue; } else if (correlatedResSyncInfos.Length == 0) { nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(phoneid, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nexttick = tickProvider.CreateNexttick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nexttick; correlatedResSyncInfos[0].ResSyncInfo.EndPoint = EndPoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } #endregion #region Fax if (faxResourcePayloadConatainer != null) { // calculate etag of the resource etag = EtagServices.ComputeEtag(faxResourcePayloadConatainer, true); // new etag // retrieve correlations for the current identity from synch storage correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { faxId }); // if no correlation exists AND resource has been deleted: // -> continue with next id // else if no correlation exists: // -> get next tick, create new correlation and add correlation to synch store // otherwise and if the etag stored in synch store is different than etag previously calculated: // -> get next tick, modify existing correlation and update it in synch store. if (correlatedResSyncInfos.Length == 0 && faxResourcePayloadConatainer.IsDeleted) { continue; } else if (correlatedResSyncInfos.Length == 0) { nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(faxId, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nexttick = tickProvider.CreateNexttick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nexttick; correlatedResSyncInfos[0].ResSyncInfo.EndPoint = EndPoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } #endregion } #endregion } else { string id; FeedEntry resourcePayloadContainer; CorrelatedResSyncInfo[] correlatedResSyncInfos; string etag; // iterate through the collection of ids of all changed resources. for (int index = 0; index < changedIdentites.Length; index++) { // current id from iterated collection id = changedIdentites[index].Id; // get resource payload container for the current identity so that we can calculated the // etag. // continue with next identity if no resource was found. resourcePayloadContainer = wrapper.GetFeedEntry(id); // calculate etag of the current resource payload etag = EtagServices.ComputeEtag(resourcePayloadContainer, true); // retrieve correlations for the current identity from synch storage correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { id }); // if no correlation exists AND resource has been deleted: // -> continue with next id // else if no correlation exists: // -> get next tick, create new correlation and add correlation to synch store // otherwise and if the etag stored in synch store is different than etag previously calculated: // -> get next tick, modify existing correlation and update it in synch store. if (resourcePayloadContainer == null || (correlatedResSyncInfos.Length == 0 && resourcePayloadContainer.IsDeleted)) { continue; } else if (correlatedResSyncInfos.Length == 0) { nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(id, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nexttick = tickProvider.CreateNexttick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nexttick; correlatedResSyncInfos[0].ResSyncInfo.EndPoint = EndPoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } } #region store next token appBookmarkInfoStore.Put(resourceKind, nextToken); #endregion // set tracking phase lock (_parentPerformer._asyncStateObj) { _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.GETCHANGESBYtick; } if (null != targetDigest) { ICorrelatedResSyncInfoEnumerator enumerator; List <string> EndPoints = new List <string>(); #warning remove this workaround if (targetDigest.Entries != null) { foreach (DigestEntry targetDigestEntry in targetDigest.Entries) { EndPoints.Add(targetDigestEntry.EndPoint); enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, targetDigestEntry.EndPoint, ((int)targetDigestEntry.Tick) - 1); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } } SyncDigestInfo sourceSyncDigestInfo = syncDigestStore.Get(resourceKind); foreach (SyncDigestEntryInfo digestEntry in sourceSyncDigestInfo) { if (EndPoints.Contains(digestEntry.EndPoint)) { continue; } EndPoints.Add(digestEntry.EndPoint); enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, digestEntry.EndPoint, -1); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } if (!EndPoints.Contains(EndPoint)) { enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, EndPoint, -1); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } } // Set tracking phase to FINISH lock (_parentPerformer._asyncStateObj.Tracking) { _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH; } }
// Asynchronous called method private void Execute(NorthwindConfig config, SyncFeedDigest syncDigestInfo) { #region Declaration SdataContext sdataContext; SupportedResourceKinds resource; IAppBookmarkInfoStore appBookmarkInfoStore; ICorrelatedResSyncInfoStore correlatedResSyncInfoStore; ISyncSyncDigestInfoStore syncDigestStore; ISyncTickProvider tickProvider; string resourceKind; string endpoint; int nextTick = 0; Token lastToken; Token nextToken; Identity[] changedIdentites; IEntityWrapper wrapper; #endregion #region init sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind; appBookmarkInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext); correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext); syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); tickProvider = RequestReceiver.NorthwindAdapter.StoreLocator.GetTickProvider(sdataContext); wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); #endregion #region get last token or create a new one if (!appBookmarkInfoStore.Get <Token>(resourceKind, out lastToken)) { lastToken = new Token(); lastToken.InitRequest = true; } #endregion #region Get local identities of changed entries since last synchronisation changedIdentites = wrapper.Entity.GetLastChanges(lastToken, config, out nextToken); #endregion if (resource == SupportedResourceKinds.phoneNumbers) { #region workaround for phones for (int index = 0; index < changedIdentites.Length; index++) { string phoneid = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix; string faxId = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix; // receive the feed entry for local identity SyncFeedEntry phoneentry = wrapper.GetFeedEntry(phoneid); SyncFeedEntry faxentry = wrapper.GetFeedEntry(faxId); if (phoneentry == null && faxentry == null) { continue; } // receive the correlation for the local identity if (phoneentry != null) { CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { phoneid }); string etag = EtagServices.ComputeEtag(phoneentry.Payload, true); // new etag if (correlatedResSyncInfos.Length == 0) { nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(phoneid, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nextTick = tickProvider.CreateNextTick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nextTick; correlatedResSyncInfos[0].ResSyncInfo.Endpoint = endpoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } if (faxentry != null) { CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { faxId }); string etag = EtagServices.ComputeEtag(faxentry.Payload, true); // new etag if (correlatedResSyncInfos.Length == 0) { nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(faxId, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nextTick = tickProvider.CreateNextTick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nextTick; correlatedResSyncInfos[0].ResSyncInfo.Endpoint = endpoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } } #endregion } else { for (int index = 0; index < changedIdentites.Length; index++) { string id = changedIdentites[index].Id; // receive the feed entry for local identity SyncFeedEntry entry = wrapper.GetFeedEntry(id); if (entry == null) { continue; } // receive the correlation for the local identity CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { id }); string etag = EtagServices.ComputeEtag(entry.Payload, true); // new etag if (correlatedResSyncInfos.Length == 0) { nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick ResSyncInfo resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now); CorrelatedResSyncInfo info = new CorrelatedResSyncInfo(id, resyncInfo); correlatedResSyncInfoStore.Put(resourceKind, info); syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo); } else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag)) { nextTick = tickProvider.CreateNextTick(resourceKind); correlatedResSyncInfos[0].ResSyncInfo.Etag = etag; correlatedResSyncInfos[0].ResSyncInfo.Tick = nextTick; correlatedResSyncInfos[0].ResSyncInfo.Endpoint = endpoint; correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now; correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]); syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo); } } } #region store next token appBookmarkInfoStore.Put(resourceKind, nextToken); #endregion // set tracking phase lock (_parentPerformer._asyncStateObj) { _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.GETCHANGESBYTICK; } // Receive syncDigestInfo if (null != syncDigestInfo) { ICorrelatedResSyncInfoEnumerator enumerator; List <string> endpoints = new List <string>(); foreach (SyncFeedDigestEntry digestEntry in syncDigestInfo.Entries) { endpoints.Add(digestEntry.Endpoint); enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, digestEntry.Endpoint, digestEntry.Tick - 2); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } SyncDigestInfo sourceSyncDigestInfo = syncDigestStore.Get(resourceKind); foreach (SyncDigestEntryInfo digestEntry in sourceSyncDigestInfo) { if (endpoints.Contains(digestEntry.Endpoint)) { continue; } endpoints.Add(digestEntry.Endpoint); enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, digestEntry.Endpoint, -1); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } if (!endpoints.Contains(endpoint)) { enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, endpoint, -1); while (enumerator.MoveNext()) { // No lock needed, as we expect that CorrelatedResSyncInfos list is // only acceeded anywhere else when Tracking phase is 'finish'. //lock(_parentPerformer._asyncStateObj) //{ _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current); //} } } } // Set tracking phase lock (_parentPerformer._asyncStateObj.Tracking) { _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH; } }
// Asynchronous called method private void Execute(NorthwindConfig config, SyncFeed feed) { #region Declarations SdataContext sdataContext; SupportedResourceKinds resource; IAppBookmarkInfoStore appBookmarkInfoStore; ICorrelatedResSyncInfoStore correlatedResSyncInfoStore; ISyncSyncDigestInfoStore syncDigestStore; GuidConverter guidConverter = new GuidConverter(); string resourceKind; string endpoint; SyncFeedDigest sourceDigest; SyncDigestInfo targetDigest; IEntityWrapper wrapper; #endregion #region init sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind; appBookmarkInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext); correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext); syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); sourceDigest = feed.Digest; targetDigest = syncDigestStore.Get(resourceKind); wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); #endregion #region process entries bool sourceIsDeleteMode; SdataTransactionResult sdTrResult; SyncState sourceState; SyncState targetState; foreach (SyncFeedEntry entry in feed.Entries) { sdTrResult = null; try { // Check whether the source entry had been deleted. // if not we expect a payload! // The variable 'sourceIsDeleteMode' holds the result of this check for later calls. if (entry.HttpMethod.Equals("DELETE", StringComparison.InvariantCultureIgnoreCase)) { sourceIsDeleteMode = true; } else if (null == entry.Payload) { throw new Exception("Payload missing."); } else { sourceIsDeleteMode = false; } // set the uuid to the payload.SyncUuid property if (!sourceIsDeleteMode) { entry.Payload.SyncUuid = entry.Uuid; } // get the source syncstate sourceState = entry.SyncState; // Look into the store to get all correlations of the uuid received from source CorrelatedResSyncInfo[] corelations = correlatedResSyncInfoStore.GetByUuid(resourceKind, new Guid[] { entry.Uuid }); if (corelations.Length == 0) { if (sourceIsDeleteMode) { // the source entry had been deleted and no correlation exists for this // entry in the target. // So we do nothing here. sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.OK; sdTrResult.HttpMessage = "OK"; sdTrResult.HttpMethod = "DELETE"; sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } else { // no target entry exists for the updated or added source entry. // So we add the source entry to the application and add a new correlation to the sync store. // If this operation fails we will have to set the result state to conflict. try { // add the entry to application sdTrResult = wrapper.Add(entry.Payload, entry.SyncLinks); if ((sdTrResult != null) && ((sdTrResult.HttpStatus == HttpStatusCode.OK) || (sdTrResult.HttpStatus == HttpStatusCode.Created))) { string etag = EtagServices.ComputeEtag(entry.Payload, true); ResSyncInfo resSyncInfo = new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint, entry.SyncState.Tick, etag, entry.SyncState.Stamp); CorrelatedResSyncInfo correlatedResSyncInfo = new CorrelatedResSyncInfo(sdTrResult.LocalId, resSyncInfo); // store the new correlation to the sync store correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfo); // update the sync digest entry if the tick of the source entry is newer than the actual tick of the target. syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfo.ResSyncInfo); } } catch (Exception e) { // in case of an unexpected error while adding a new entry to target // we create a transaction result with the state 'Conflict' sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.Conflict; sdTrResult.HttpMethod = "POST"; sdTrResult.HttpMessage = e.ToString(); sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } } } else { // a correlation was found for the source entry. #region update or delete try { bool doUpdate = false; //bool doDelete = false; bool isConflict = true; // set the LocalID from correlation to the entry.Payload.LocalID property // only if source had not been deleted. if (!sourceIsDeleteMode) { entry.Payload.LocalID = corelations[0].LocalId; } targetState = Helper.GetSyncState(corelations[0]); //If sourceState.endpoint = targetState.endpoint, //there is no conflict and the update must be applied //if sourceState.tick > targetState.tick. if (targetState.Endpoint.Equals(sourceState.Endpoint, StringComparison.InvariantCultureIgnoreCase)) { isConflict = false; if (sourceState.Tick > targetState.Tick) { //if (!sourceIsDeleteMode) doUpdate = true; //else // doDelete = true; } } else { SyncState sourceDigestSyncState = Helper.GetSyncState(sourceDigest, targetState.Endpoint);; SyncState targetDigestSyncState = Helper.GetSyncState(targetDigest, sourceState.Endpoint); //If targetState is contained in sourceDigest, //i.e. if sourceDigest has a digest entry E such that E.endpoint = targetState.endpoint //and E.tick >= targetState.tick, there is no conflict and the update must be applied. if (sourceDigestSyncState != null) { if (sourceDigestSyncState.Tick > targetState.Tick) { doUpdate = true; isConflict = false; } } //If sourceState is contained in targetDigest, //i.e. if targetDigest has a digest entry E such that E.endpoint = sourceState.endpoint //and E.tick >= sourceState.tick, there is no conflict and the update must be ignored //(target has the most recent version). if (targetDigestSyncState != null) { if (targetDigestSyncState.Tick > sourceState.Tick) { doUpdate = false; isConflict = false; } } //Otherwise (targetState not contained in sourceDigest, sourceState not contained in targetDigest), //there is a conflict. if ((sourceDigestSyncState == null) && (targetDigestSyncState == null)) { isConflict = true; } } //****************** Conflict **************** //In case of conflict, the target endpoint uses the following algorithm to resolve the conflict: //Let sourceEntry be the sourceDigest digest entry such that sourceEntry.endpoint = sourceState.endpoint. //Let targetEntry be the targetDigest digest entry such that targetEntry.endpoint = targetState.endpoint. //If sourceEntry .conflictPriority <> targetEntry .conflictPriority, the side with lowest priority wins. if (isConflict) { int sourceConflictPriority = Helper.GetConflictPriority(sourceDigest, sourceState.Endpoint); int targetConflictPriority = Helper.GetConflictPriority(targetDigest, targetState.Endpoint); if (sourceConflictPriority > targetConflictPriority) { doUpdate = true; } else if (sourceConflictPriority < targetConflictPriority) { doUpdate = false; } else { //Otherwise (sourceEntry .conflictPriority = targetEntry .conflictPriority), //then sourceState.stamp and targetState.stamp are compared //and the entry with the most recent timestamp wins. if (sourceState.Stamp > targetState.Stamp) { doUpdate = true; } } } ResSyncInfo resSyncInfo = new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint, entry.SyncState.Tick, "", entry.SyncState.Stamp); if (doUpdate && !sourceIsDeleteMode) { // update the entry in the application and update the sync store sdTrResult = wrapper.Update(entry.Payload, entry.SyncLinks); if ((sdTrResult != null) && (sdTrResult.HttpStatus == HttpStatusCode.OK)) { string etag = EtagServices.ComputeEtag(entry.Payload, true); resSyncInfo = new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint, entry.SyncState.Tick, etag, entry.SyncState.Stamp); CorrelatedResSyncInfo correlatedResSyncInfo = new CorrelatedResSyncInfo(sdTrResult.LocalId, resSyncInfo); correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfo); } } else if (!doUpdate && !sourceIsDeleteMode) { sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.Conflict; sdTrResult.HttpMethod = "PUT"; sdTrResult.HttpMessage = ""; sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } else if (doUpdate && sourceIsDeleteMode) { // delete the entry in the application and update the sync store // we do not support nested deletion. So only TradingAccounts and SalesOrders can be deleted. if (resource == SupportedResourceKinds.tradingAccounts || resource == SupportedResourceKinds.salesOrders) { sdTrResult = wrapper.Delete(corelations[0].LocalId); if ((sdTrResult != null) && (sdTrResult.HttpStatus == HttpStatusCode.OK)) { correlatedResSyncInfoStore.Delete(resource.ToString(), corelations[0].ResSyncInfo.Uuid); } } else { // this does not correspond to real fact, what we did at target side! sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.OK; sdTrResult.HttpMessage = "OK"; sdTrResult.HttpMethod = "DELETE"; sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } } else { sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.Conflict; sdTrResult.HttpMessage = ""; sdTrResult.HttpMethod = "DELETE"; sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } syncDigestStore.PersistNewer(resourceKind, resSyncInfo); } catch (Exception e) { sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.Conflict; sdTrResult.HttpMethod = "PUT"; sdTrResult.HttpMessage = e.ToString(); sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } #endregion } } catch (Exception e) { sdTrResult = new SdataTransactionResult(); sdTrResult.HttpStatus = HttpStatusCode.Conflict; sdTrResult.HttpMessage = e.ToString(); sdTrResult.ResourceKind = resource; sdTrResult.Uuid = entry.Uuid; } #region store transaction result if (sdTrResult != null) { lock (_parentPerformer._asyncStateObj) { this._parentPerformer._asyncStateObj.TransactionResults.Add(sdTrResult); } } #endregion } #endregion // Set tracking phase lock (_parentPerformer._asyncStateObj.Tracking) { _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH; } }