/// **************************************************************** /// public Delete /// ---------------------------------------------------------------- /// <summary> /// </summary> /// **************************************************************** /// public override void Delete() { Debug.Enter(); SqlStoredProcedureAccessor sp = new SqlStoredProcedureAccessor("net_businessEntity_delete"); sp.Parameters.Add("@PUID", SqlDbType.NVarChar, UDDI.Constants.Lengths.UserID); sp.Parameters.Add("@businessKey", SqlDbType.UniqueIdentifier); sp.Parameters.Add("@contextID", SqlDbType.UniqueIdentifier); sp.Parameters.SetString("@PUID", Context.User.ID); sp.Parameters.SetGuidFromString("@businessKey", BusinessKey); sp.Parameters.SetGuid("@contextID", Context.ContextID); sp.ExecuteNonQuery(); // // Save the change log entry. // if (Context.LogChangeRecords) { ChangeRecord changeRecord = new ChangeRecord(); changeRecord.Payload = new ChangeRecordDelete(EntityType.BusinessEntity, BusinessKey); changeRecord.Log(); } Debug.Leave(); }
public static ChangeRecord EvaluatePropertyChange(this ChangeRequest changeRequest, object item, string property, string newValue, bool isRelated = false, string comment = "") { var oldValue = item.GetPropertyValue(property); if (oldValue.ToStringSafe().IsNullOrWhiteSpace() && newValue.IsNullOrWhiteSpace()) { return(null); } if (!(oldValue is string) || ( string )oldValue != newValue) { var changeRecord = new ChangeRecord() { OldValue = oldValue.ToStringSafe(), NewValue = newValue, Action = ChangeRecordAction.Update, IsRejected = false, WasApplied = false, Property = property, Comment = comment }; if (isRelated && item is IEntity) { var entity = ( IEntity )item; changeRecord.RelatedEntityId = entity.Id; changeRecord.RelatedEntityTypeId = EntityTypeCache.Get(entity.GetType()).Id; } changeRequest.ChangeRecords.Add(changeRecord); return(changeRecord); } return(null); }
private async Task <CdcState> SetInitialStateAsync(CancellationToken token, IKafkaProducer producer, string executionId, TableSchema tableSchema, TimeSpan maxInterval) { byte[] initialToLsn = await _cdcReaderClient.GetMaxLsnAsync(); var existingOffset = await _cdcReaderClient.GetLastCdcOffsetAsync(executionId, tableSchema.TableName); if (existingOffset.Result == Result.NoStoredState) { Console.WriteLine($"Table {tableSchema.TableName} - No previous stored LSN. Starting from first change"); var hasFirstChange = false; ChangeBatch syncBatch = null; ChangeRecord firstChange = null; while (!hasFirstChange && !token.IsCancellationRequested) { var initialFromLsn = await _cdcReaderClient.GetMinValidLsnAsync(tableSchema.TableName); initialToLsn = await _cdcReaderClient.GetMaxLsnAsync(); syncBatch = await _cdcReaderClient.GetChangeBatchAsync(tableSchema, initialFromLsn, initialToLsn, 1); if (syncBatch.Changes.Any()) { firstChange = syncBatch.Changes.First(); await producer.SendAsync(token, firstChange); hasFirstChange = true; } else { await Task.Delay(maxInterval); } } var cdcState = new CdcState() { FromLsn = firstChange.Lsn, FromSeqVal = firstChange.SeqVal, ToLsn = initialToLsn, UnfinishedLsn = syncBatch.MoreOfLastTransaction }; var offset = GetOffset(cdcState); await _cdcReaderClient.StoreCdcOffsetAsync(executionId, tableSchema.TableName, offset); return(cdcState); } else { Console.WriteLine($"Table {tableSchema.TableName} - Starting from stored LSN"); return(new CdcState() { FromLsn = existingOffset.State.Lsn, FromSeqVal = existingOffset.State.SeqVal, ToLsn = initialToLsn, UnfinishedLsn = existingOffset.State.UnfinishedLsn }); } }
/// <summary> /// Add a formatted message representing a change in a lookup. /// </summary> public void AddChangedLookup(string relName, IEntity oldValue, IEntity newValue) { if (oldValue == null && newValue == null) { throw new ArgumentException(); } var change = new ChangeRecord { Field = relName }; _changes.Add(change); if (oldValue == null) { change.UpdateType = "set"; } else if (newValue == null) { change.UpdateType = "cleared"; } else { change.UpdateType = "changed"; } var sb = new StringBuilder(); AddEntityFromTo(sb, oldValue, newValue); change.Summary = sb.ToString(); }
public static ChangeRecord EvaluatePropertyChange(this ChangeRequest changeRequest, object item, string property, Enum newValue, bool isRelated = false, string comment = "") { var oldValue = item.GetPropertyValue(property); if (oldValue == null && newValue == null) { return(null); } if (!(oldValue is Enum) || !newValue.Equals(( Enum )oldValue)) { var changeRecord = new ChangeRecord() { OldValue = (( Enum )oldValue).ConvertToInt().ToString(), NewValue = newValue.ConvertToInt().ToString(), Action = ChangeRecordAction.Update, IsRejected = false, WasApplied = false, Property = property, Comment = comment }; if (isRelated && item is IEntity) { var entity = ( IEntity )item; changeRecord.RelatedEntityId = entity.Id; changeRecord.RelatedEntityTypeId = EntityTypeCache.Get(entity.GetType()).Id; } changeRequest.ChangeRecords.Add(changeRecord); return(changeRecord); } return(null); }
/// <summary> /// Handle a MongoDB change record. /// </summary> /// <param name="change">The change record.</param> /// <remarks> /// This method is called on a thread-pool background thread. /// </remarks> void IHandleChange.HandleChange(ChangeRecord change) { // workers not started if (!_workers.IsValueCreated) { return; } // look for state changed to queued if (!IsQueued(change)) { return; } // find first free worker and trigger work var worker = NextWorker(); if (worker == null) { _logger.Trace() .Message("Change notification trigger: All workers busy") .Write(); return; } _logger.Trace() .Message("Change notification trigger: {0}", worker.Name) .Write(); worker.Trigger(); }
void DisplayChangeRecord(ChangeRecord changeRecord) { UTF8EncodedStringWriter writer = new UTF8EncodedStringWriter(); changeRecordSerializer.Serialize(writer, changeRecord); writer.Close(); changeRecordTextBox.Text = writer.ToString(); }
private ChangeRecord GetNoDataSignalRecord(TableSchema tableSchema) { var noDataSignalRecord = new ChangeRecord(); noDataSignalRecord.Lsn = new byte[10]; noDataSignalRecord.LsnStr = "0"; noDataSignalRecord.SeqVal = new byte[10]; noDataSignalRecord.TableName = tableSchema.TableName; return(noDataSignalRecord); }
public RowChange Convert(ChangeRecord changeRecord) { return new RowChange() { ChangeType = changeRecord.ChangeType, Data = changeRecord.Data, ChangeKey = changeRecord.ChangeKey, Lsn = changeRecord.LsnStr, SeqVal = changeRecord.SeqValStr }; }
static ChangeRecord CreateCorrection(ChangeRecord originalChangeRecord, BusinessEntity businessEntity) { ChangeRecordNewData changeRecordNewData = originalChangeRecord.Payload as ChangeRecordNewData; changeRecordNewData.Entity = businessEntity; ChangeRecordCorrection changeRecordCorrection = new ChangeRecordCorrection(); changeRecordCorrection.ChangeRecord = originalChangeRecord; return(new ChangeRecord(changeRecordCorrection)); }
void DisplayCorrection(ChangeRecord changeRecord) { ChangeRecordCorrection changeRecordCorrection = new ChangeRecordCorrection(); changeRecordCorrection.ChangeRecord = changeRecord; UTF8EncodedStringWriter writer = new UTF8EncodedStringWriter(); correctionSerializer.Serialize(writer, changeRecordCorrection); writer.Close(); correctionTextBox.Text = writer.ToString(); }
/// <summary> /// Initializes a new instance of the <see cref="ChangeNotification"/> class. /// </summary> /// <param name="change">The change.</param> /// <exception cref="System.ArgumentNullException"></exception> public ChangeNotification(ChangeRecord change) { if (change == null) { throw new ArgumentNullException(nameof(change)); } Timestamp = GetDateTime(change.Timestamp); UniqueId = change.UniqueId; Version = change.Version; Operation = change.Operation; Namespace = change.Namespace; Key = GetKey(change)?.ToString(); }
/// **************************************************************** /// public Save /// ---------------------------------------------------------------- /// <summary> /// Stores the bindingTemplate details into the database, as a /// child of the service specified by the key. /// </summary> /// **************************************************************** /// public override void Save() { Validate(); InnerSave(this.ServiceKey); // // Save the change log entry. // if (Context.LogChangeRecords) { ChangeRecord changeRecord = new ChangeRecord(); changeRecord.Payload = new ChangeRecordNewData(this); changeRecord.Log(); } }
private static bool IsUpdateQueued(ChangeRecord change) { if (change.Document == null) { return(false); } if (!change.Document.Contains("$set")) { return(false); } var document = change.Document["$set"].AsBsonDocument; return(IsQueued(document)); }
private void correctionButton_Click(object sender, System.EventArgs e) { if (false == ValidatePublisher()) { return; } try { ConnectionManager.BeginTransaction(); // // Deserialize into a change record object // StringReader reader = new StringReader(correctionTextBox.Text); ChangeRecordCorrection changeRecordCorrection = ( ChangeRecordCorrection )correctionSerializer.Deserialize(reader); // // Validate what we created. // SchemaCollection.Validate(changeRecordCorrection); // // Create a new change record to hold the correction. // ChangeRecord changeRecord = new ChangeRecord(changeRecordCorrection); changeRecord.Process(); ConnectionManager.Commit(); // // If we made it this far, we were able to process the correction // MessageBox.Show("Correction processed!"); // // Refresh our display. // ShowChangeRecord(); } catch (Exception exception) { ConnectionManager.Abort(); MessageBox.Show("An exception occurred when trying to process the correction:\r\n\r\n" + exception.ToString()); } }
public ICollection <ChangeRecord> GetChanges(ChangeDto filter) { var query = new ChangeRecord { Id = filter.Id, ChangedProperties = filter.ChangedProperties, IsActive = filter.IsActive, SourceId = filter.SourceId, }; Enum.TryParse(filter.OperationType, out OperationTypeEnum operationType); Enum.TryParse(filter.RecordType, out ChangeRecordTypeEnum recordType); query.OperationType = operationType; query.RecordType = recordType; return(_changeManager.GetchangeRecords(query)); }
public static List <ChangeRecord> EvaluateAttributes(this ChangeRequest changeRequest, IEntity entity, bool isRelated = false, string comment = "") { var changeRecords = new List <ChangeRecord>(); if (!(entity is IHasAttributes) || entity.Id == 0) { return(null); } var ihaEntity = entity as IHasAttributes; RockContext rockContext = new RockContext(); var entityService = Reflection.GetServiceForEntityType(EntityTypeCache.Get(ihaEntity.GetType()).GetEntityType(), rockContext); MethodInfo queryableMethodInfo = entityService.GetType().GetMethod("Queryable", new Type[] { }); IQueryable <IEntity> entityQuery = queryableMethodInfo.Invoke(entityService, null) as IQueryable <IEntity>; var currentModel = ( IHasAttributes )entityQuery.Where(x => x.Id == ihaEntity.Id).FirstOrDefault(); currentModel.LoadAttributes(); foreach (var cAttribute in currentModel.Attributes) { if (currentModel.GetAttributeValue(cAttribute.Key) != ihaEntity.GetAttributeValue(cAttribute.Key)) { var changeRecord = new ChangeRecord { OldValue = currentModel.GetAttributeValue(cAttribute.Key), NewValue = ihaEntity.GetAttributeValue(cAttribute.Key), IsRejected = false, WasApplied = false, Action = ChangeRecordAction.Attribute, Property = cAttribute.Key, Comment = comment }; if (isRelated) { changeRecord.RelatedEntityId = entity.Id; changeRecord.RelatedEntityTypeId = EntityTypeCache.Get(entity.GetType()).Id; } changeRequest.ChangeRecords.Add(changeRecord); changeRecords.Add(changeRecord); } } return(changeRecords); }
private async Task <long> PublishAsync(IKafkaProducer producer, CancellationToken token, FullLoadBatch batch, long ctr) { foreach (var row in batch.Records) { var change = new ChangeRecord(); change.ChangeKey = row.ChangeKey; change.ChangeType = ChangeType.INSERT; change.LsnStr = ctr.ToString(); change.SeqValStr = ctr.ToString(); change.Data = row.Data; await producer.SendAsync(token, change); ctr++; } return(ctr); }
private void RecordChange(ChangeRecord change) { if (this.changes.Count > 0) { var last = this.changes[this.changes.Count - 1]; if (last.Range.Span.End == change.Range.Span.Start) { // merge changes... this.changes[this.changes.Count - 1] = new ChangeRecord( new TextChangeRange(new TextSpan(last.Range.Span.Start, last.Range.Span.Length + change.Range.Span.Length), last.Range.NewLength + change.Range.NewLength), Combine(last.NewNodes, change.NewNodes) ); return; } System.Diagnostics.Debug.Assert(change.Range.Span.Start >= last.Range.Span.End); } this.changes.Add(change); }
public static ChangeDto ConvertChangeToDto(ChangeRecord change) { if (change != null) { var dto = new ChangeDto { Id = change.Id, ChangedProperties = change.ChangedProperties, CreateTime = change.CreateTime, IsActive = change.IsActive ?? false, SourceId = change.SourceId, UpdateTime = change.UpdateTime, OperationType = change.OperationType.ToString(), RecordType = change.RecordType.ToString(), }; } return(null); }
private async Task <ChangeRecord> AddChangeRecord(string name, UserDigest user, string action, T before, T after, DateTime now) { var changeRecord = new ChangeRecord { name = name, changedById = user.Id, changedByName = user.DisplayName, changedOn = now, subjectDescription = after != null ? after.type : before.type, subjectId = after != null ? after.id : before.id, subjectType = after != null ? after.type : before.type, action = action, before = before, after = after }; var doc = await _documentClient.CreateDocumentAsync(CollectionUri, changeRecord); return((ChangeRecord)(dynamic)doc.Resource); }
private static BsonValue GetKey(ChangeRecord change) { if (change?.Document == null) { return(false); } // insert, delete or update if (change.Operation == "i" || change.Operation == "d") { return(GetKey(change.Document)); } if (change.Operation == "u") { return(GetKey(change.Query)); } return(null); }
private static bool IsQueued(ChangeRecord change) { if (change?.Document == null) { return(false); } // only insert or update if (change.Operation == "i") { return(IsInsertQueued(change)); } if (change.Operation == "u") { return(IsUpdateQueued(change)); } return(false); }
static void FixChangeRecords(BusinessEntity businessEntity) { // // Get all related change records // ArrayList newDataChangeRecords = GetChangeRecordsForEntity(businessEntity); // // Create and process a correction for each change record. // Log("\t\tSTART Processing Corrections"); foreach (ChangeRecord changeRecord in newDataChangeRecords) { ChangeRecord changeRecordCorrection = CreateCorrection(changeRecord, businessEntity); changeRecordCorrection.Process(); LogChangeRecordCorrection(changeRecord, changeRecordCorrection); } Log("\t\tDONE Processing Corrections"); }
private void RecordChange(ChangeRecord change) { if (_changes.Count > 0) { var last = _changes[_changes.Count - 1]; if (last.Range.Span.End == change.Range.Span.Start) { // merge changes... _changes[_changes.Count - 1] = new ChangeRecord( new TextChangeRange(new TextSpan(last.Range.Span.Start, last.Range.Span.Length + change.Range.Span.Length), last.Range.NewLength + change.Range.NewLength), Combine(last.OldNodes, change.OldNodes), Combine(last.NewNodes, change.NewNodes)); return; } Debug.Assert(change.Range.Span.Start >= last.Range.Span.End); } _changes.Add(change); }
public async Task SendAsync(CancellationToken token, ChangeRecord changeRecord) { var change = Convert(changeRecord); var jsonText = JsonConvert.SerializeObject(change); var sent = false; while (!sent && !token.IsCancellationRequested) { var sendResult = await _producer.ProduceAsync(topic : _topic, key : null, val : jsonText, blockIfQueueFull : true); if (sendResult.Error.HasError) { Console.WriteLine("Could not send: " + sendResult.Error.Reason); await Task.Delay(100); } else { sent = true; } } }
public static ChangeRecord AddEntity(this ChangeRequest changeRequest, IEntity entity, RockContext rockContext, bool isRelated = false, string comment = "") { ChangeRecord changeRecord = new ChangeRecord { OldValue = "", NewValue = entity.ToJson(), IsRejected = false, WasApplied = false, Action = ChangeRecordAction.Create, Comment = comment }; if (isRelated) { changeRecord.RelatedEntityId = 0; changeRecord.RelatedEntityTypeId = EntityTypeCache.Get(entity.GetType()).Id; } changeRequest.ChangeRecords.Add(changeRecord); return(changeRecord); }
public async Task SendAsync(CancellationToken token, ChangeRecord changeRecord) { var change = Convert(changeRecord); var record = _avroTypeConverter.GetRecord(_tableSchema, change); var sent = false; while (!sent && !token.IsCancellationRequested) { var sendResult = await _producer.ProduceAsync(topic : _topic, key : null, val : record, blockIfQueueFull : true); if (sendResult.Error.HasError) { Console.WriteLine("Could not send: " + sendResult.Error.Reason); await Task.Delay(100); } else { sent = true; } } }
public void Notify(ChangeRecord <T> record) { var connection = new Connection(_address); var session = new Session(connection); var sender = new SenderLink(session, GetType().Name, $"{_topicPrefix}{typeof(T).Name}"); var json = JsonConvert.SerializeObject(record); var header = new Header { Ttl = _ttlMins * 60 * 1000 }; var data = new Data { Binary = Encoding.UTF8.GetBytes(json) }; var msg = new Message { Header = header, BodySection = data }; sender.Send(msg); }
public void Update(Solutions solution) { _validator.ValidateAndThrowEx(solution, ruleSet: nameof(ISolutionsLogic.Update)); _modifier.ForUpdate(solution); var oldSoln = _datastore.ById(solution.Id); _datastore.Update(solution); var contact = _contacts.ByEmail(Context.Email()); var record = new ChangeRecord <Solutions>(contact.Id, oldSoln, solution); _notifier.Notify(record); // TODO remove this code once we have activated SolutionChangeReceiver // create SharePoint folder structure if (solution.Status == SolutionStatus.Registered) { _evidenceBlobStoreLogic.PrepareForSolution(solution.Id); } }
public static bool Changes(out List<ChangeRecord> ChangeRecords, string CommandLine, bool AllowSpew = true, bool UseCaching = false) { if (UseCaching && ChangesCache.ContainsKey(CommandLine)) { ChangeRecords = ChangesCache[CommandLine]; return true; } ChangeRecords = new List<ChangeRecord>(); CheckP4Enabled(); try { // Change 1999345 on 2014/02/16 by buildmachine@BuildFarm_BUILD-23_buildmachine_++depot+UE4 'GUBP Node Shadow_LabelPromotabl' string Output; if (!LogP4Output(out Output, "changes " + CommandLine, null, AllowSpew)) { return false; } var Lines = Output.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (var Line in Lines) { ChangeRecord Change = new ChangeRecord(); string MatchChange = "Change "; string MatchOn = " on "; string MatchBy = " by "; int ChangeAt = Line.IndexOf(MatchChange); int OnAt = Line.IndexOf(MatchOn); int ByAt = Line.IndexOf(MatchBy); int AtAt = Line.IndexOf("@"); int TickAt = Line.IndexOf("'"); int EndTick = Line.LastIndexOf("'"); if (ChangeAt >= 0 && OnAt > ChangeAt && ByAt > OnAt && TickAt > ByAt && EndTick > TickAt) { var ChangeString = Line.Substring(ChangeAt + MatchChange.Length, OnAt - ChangeAt - MatchChange.Length); Change.CL = int.Parse(ChangeString); if (Change.CL < 1990000) { throw new AutomationException("weird CL {0} in {1}", Change.CL, Line); } Change.User = Line.Substring(ByAt + MatchBy.Length, AtAt - ByAt - MatchBy.Length); Change.Summary = Line.Substring(TickAt + 1, EndTick - TickAt - 1); Change.UserEmail = UserToEmail(Change.User); ChangeRecords.Add(Change); } } } catch (Exception) { return false; } ChangeRecords.Sort((A, B) => ChangeRecord.Compare(A, B)); ChangesCache.Add(CommandLine, ChangeRecords); return true; }
public static int Compare(ChangeRecord A, ChangeRecord B) { return (A.CL < B.CL) ? -1 : (A.CL > B.CL) ? 1 : 0; }
public bool Changes(out List<ChangeRecord> ChangeRecords, string CommandLine, bool AllowSpew = true, bool UseCaching = false, bool LongComment = false, bool WithClient = false) { // If the user specified '-l' or '-L', the summary will appear on subsequent lines (no quotes) instead of the same line (surrounded by single quotes) bool ContainsDashL = CommandLine.StartsWith("-L ", StringComparison.InvariantCultureIgnoreCase) || CommandLine.IndexOf(" -L ", StringComparison.InvariantCultureIgnoreCase) > 0; bool bSummaryIsOnSameLine = !ContainsDashL; if (bSummaryIsOnSameLine && LongComment) { CommandLine = "-L " + CommandLine; bSummaryIsOnSameLine = false; } if (UseCaching && ChangesCache.ContainsKey(CommandLine)) { ChangeRecords = ChangesCache[CommandLine]; return true; } ChangeRecords = new List<ChangeRecord>(); CheckP4Enabled(); try { // Change 1999345 on 2014/02/16 by buildmachine@BuildFarm_BUILD-23_buildmachine_++depot+UE4 'GUBP Node Shadow_LabelPromotabl' string Output; if (!LogP4Output(out Output, "changes " + CommandLine, null, AllowSpew, WithClient: WithClient)) { throw new AutomationException("P4 returned failure."); } var Lines = Output.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); for(int LineIndex = 0; LineIndex < Lines.Length; ++LineIndex) { var Line = Lines[ LineIndex ]; // If we've hit a blank line, then we're done if( String.IsNullOrEmpty( Line ) ) { break; } ChangeRecord Change = new ChangeRecord(); string MatchChange = "Change "; string MatchOn = " on "; string MatchBy = " by "; int ChangeAt = Line.IndexOf(MatchChange); int OnAt = Line.IndexOf(MatchOn); int ByAt = Line.IndexOf(MatchBy); if (ChangeAt == 0 && OnAt > ChangeAt && ByAt > OnAt) { var ChangeString = Line.Substring(ChangeAt + MatchChange.Length, OnAt - ChangeAt - MatchChange.Length); Change.CL = int.Parse(ChangeString); if (Change.CL < 1990000) { throw new AutomationException("weird CL {0} in {1}", Change.CL, Line); } int AtAt = Line.IndexOf("@"); Change.User = Line.Substring(ByAt + MatchBy.Length, AtAt - ByAt - MatchBy.Length); if( bSummaryIsOnSameLine ) { int TickAt = Line.IndexOf("'"); int EndTick = Line.LastIndexOf("'"); if( TickAt > ByAt && EndTick > TickAt ) { Change.Summary = Line.Substring(TickAt + 1, EndTick - TickAt - 1); } } else { ++LineIndex; if( LineIndex >= Lines.Length ) { throw new AutomationException("Was expecting a change summary to appear after Change header output from P4, but there were no more lines to read"); } Line = Lines[ LineIndex ]; if( !String.IsNullOrEmpty( Line ) ) { throw new AutomationException("Was expecting blank line after Change header output from P4, got {0}", Line); } ++LineIndex; for( ; LineIndex < Lines.Length; ++LineIndex ) { Line = Lines[ LineIndex ]; int SummaryChangeAt = Line.IndexOf(MatchChange); int SummaryOnAt = Line.IndexOf(MatchOn); int SummaryByAt = Line.IndexOf(MatchBy); if (SummaryChangeAt == 0 && SummaryOnAt > SummaryChangeAt && SummaryByAt > SummaryOnAt) { // OK, we found a new change. This isn't part of our summary. We're done with the summary. Back we go. //CommandUtils.Log("Next summary is {0}", Line); --LineIndex; break; } // Summary lines are supposed to begin with a single tab character (even empty lines) if( !String.IsNullOrEmpty( Line ) && Line[0] != '\t' ) { throw new AutomationException("Was expecting every line of the P4 changes summary to start with a tab character or be totally empty"); } // Remove the tab var SummaryLine = Line; if( Line.StartsWith( "\t" ) ) { SummaryLine = Line.Substring( 1 ); } // Add a CR if we already had some summary text if( !String.IsNullOrEmpty( Change.Summary ) ) { Change.Summary += "\n"; } // Append the summary line! Change.Summary += SummaryLine; } } Change.UserEmail = UserToEmail(Change.User); ChangeRecords.Add(Change); } else { throw new AutomationException("Output of 'p4 changes' was not formatted how we expected. Could not find 'Change', 'on' and 'by' in the output line: " + Line); } } } catch (Exception Ex) { CommandUtils.Log(System.Diagnostics.TraceEventType.Warning, "Unable to get P4 changes with {0}", CommandLine); CommandUtils.Log(System.Diagnostics.TraceEventType.Warning, " Exception was {0}", LogUtils.FormatException(Ex)); return false; } ChangeRecords.Sort((A, B) => ChangeRecord.Compare(A, B)); if( ChangesCache.ContainsKey(CommandLine) ) { ChangesCache[CommandLine] = ChangeRecords; } else { ChangesCache.Add(CommandLine, ChangeRecords); } return true; }