/// <summary> /// Parse CRM response to get list of updated entities with timestamps and list of updated records /// </summary> /// <param name="responseCollection">Dictionary for entityName and its RetrieveEntityChange response to parse</param> /// <returns>List of updated records</returns> private TimeBasedChangedData ParseBusinessEntityChangesResponse(Dictionary <string, RetrieveEntityChangesResponse> responseCollection, CrmDbContext context, Guid websiteId) { if (responseCollection == null || responseCollection.Count == 0) { return(null); } var changedData = new TimeBasedChangedData { UpdatedEntitiesWithLastTimestamp = new Dictionary <string, string>(), UpdatedEntityRecords = new List <IChangedItem>() }; foreach (var kvp in responseCollection) { var entityName = kvp.Key; var dataToken = kvp.Value.EntityChanges.DataToken; ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("RetrieveEntityChangesResponse received for entity: {0} with new data token: {1}", entityName, dataToken)); KeyValuePair <string, string>?entityNameWithTimestamp = new KeyValuePair <string, string>(entityName, dataToken); if (changedData.UpdatedEntitiesWithLastTimestamp.ContainsKey(entityNameWithTimestamp.Value.Key)) { continue; } changedData.UpdatedEntitiesWithLastTimestamp.Add(entityNameWithTimestamp.Value.Key, entityNameWithTimestamp.Value.Value); } changedData.UpdatedEntityRecords.AddRange(this.GetChangesRelatedToWebsite(responseCollection, context, websiteId)); return(changedData); }
/// <summary> /// Logs telemetry for the message processing for each entity record. /// Includes how long total it took from update in CRM to update in Portal /// </summary> /// <param name="pushedToCache">Time at which the records wer pushed to the cache</param> /// <param name="processingRecords">Messages that have notified the portal of changes from Azure</param> /// <param name="crmQueryResults">Record changes from CRM</param> /// <param name="timeStampTable">Timestamps of records before this current batch updated</param> private void LogTimeTelemetry(DateTime pushedToCache, Dictionary <string, EntityRecordMessage> processingRecords, TimeBasedChangedData crmQueryResults, Dictionary <string, string> timeStampTable) { var filteredProcessingRecords = processingRecords.Where(item => item.Value.MessageType == MessageType.Create || item.Value.MessageType == MessageType.Update); var filteredCrmQueryResults = crmQueryResults.UpdatedEntityRecords.OfType <Microsoft.Xrm.Sdk.NewOrUpdatedItem>() // Only add telemetry for non-initial load entities (timestamp already has been recorded. // This will avoid awful timespan values being captured on initial startup .Where(filteredResult => timeStampTable.ContainsKey(filteredResult.NewOrUpdatedEntity.LogicalName) // Timestamp table stores string dataTokens from CRM in the form of 123456!04/04/2004 04:04:04.04 ? filteredResult.NewOrUpdatedEntity.GetAttributeValue <DateTime>("modifiedon") > DateTime.Parse(timeStampTable[filteredResult.NewOrUpdatedEntity.LogicalName].Split('!')[1]) : false); IEnumerable <TimeTrackingEntry> entries = filteredProcessingRecords.Join( // With list filteredCrmQueryResults, // Keys processingRecord => processingRecord.Key, crmQueryResult => crmQueryResult.NewOrUpdatedEntity.LogicalName, // How to join (processingRecord, crmqueryResult) => new TimeTrackingEntry( processingRecord.Key, pushedToCache, crmqueryResult.NewOrUpdatedEntity.GetAttributeValue <DateTime>("modifiedon"), processingRecord.Value.Received)); entries.ToList().ForEach(traceEntry => { ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Overall the '{0}' change took {1} to propagate\n\tMessage to reach portal through Azure: {2}\n\tSleep and Retreival of change from CRM: {3}", traceEntry.EntityLogicalName, traceEntry.OverallDelta, traceEntry.AzureProcessingDelta, traceEntry.InvalidationDelta)); }); }