Skip to content

Commit ae92e7d

Browse files
committed
Refactor code formatting and optimize performance in configuration classes
1 parent a2c80ed commit ae92e7d

6 files changed

Lines changed: 95 additions & 85 deletions

File tree

src/Configuration/Command.cs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ public override void Run(ChangeInfo change, TriggerType trigger)
8484
return;
8585
}
8686

87-
if (!File.Exists(commandPath))
87+
// Check file existence once and cache result
88+
bool commandExists = File.Exists(commandPath);
89+
if (!commandExists)
8890
{
8991
Logger.WriteLine(
9092
$"{correlationPrefix}The command '{commandPath}' was not found. Command was not run.",
@@ -170,36 +172,28 @@ private void Execute(Guid correlationId)
170172
{
171173
if (_processInfo.TryDequeue(out ProcessStartInfo? startInfo))
172174
{
173-
if (File.Exists(startInfo.FileName))
175+
// File existence was already verified in Run method
176+
try
174177
{
175-
try
176-
{
177-
using (Process process = new Process())
178-
{
179-
Logger.WriteLine(
180-
$"[{correlationId}] START: Process {startInfo.FileName} {startInfo.Arguments}.");
181-
182-
process.StartInfo = startInfo;
183-
process.StartInfo.CreateNoWindow = true;
184-
process.StartInfo.UseShellExecute = false;
185-
process.Start();
186-
process.WaitForExit();
187-
188-
Logger.WriteLine(
189-
$"[{correlationId}] END: Process {process?.StartInfo.FileName} {process?.StartInfo.Arguments} has completed.");
190-
}
191-
}
192-
catch (Exception ex)
178+
using (Process process = new Process())
193179
{
194180
Logger.WriteLine(
195-
$"[{correlationId}] Could not run the command '{startInfo.FileName} {startInfo.Arguments}'. Reason: {ex.Message}",
196-
LogLevel.ERROR);
181+
$"[{correlationId}] START: Process {startInfo.FileName} {startInfo.Arguments}.");
182+
183+
process.StartInfo = startInfo;
184+
process.StartInfo.CreateNoWindow = true;
185+
process.StartInfo.UseShellExecute = false;
186+
process.Start();
187+
process.WaitForExit();
188+
189+
Logger.WriteLine(
190+
$"[{correlationId}] END: Process {process?.StartInfo.FileName} {process?.StartInfo.Arguments} has completed.");
197191
}
198192
}
199-
else
193+
catch (Exception ex)
200194
{
201195
Logger.WriteLine(
202-
$"[{correlationId}] The command '{startInfo.FileName}' was not found. Command was not run.",
196+
$"[{correlationId}] Could not run the command '{startInfo.FileName} {startInfo.Arguments}'. Reason: {ex.Message}",
203197
LogLevel.ERROR);
204198
}
205199
}

src/Configuration/HasNeedsBase.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,21 @@ public bool CanRun
4343
{
4444
// If there are no needs, then return true to indicate the task
4545
// can be run
46-
if (_needs == null)
46+
if (_needs == null || _needs.Count == 0)
4747
{
4848
return true;
4949
}
50-
else
50+
51+
// Check if all needs have been completed without LINQ allocation
52+
for (int i = 0; i < _needs.Count; i++)
5153
{
52-
// Return the value if all needs have been completed to
53-
// indicate the task can be run
54-
return _needs.All(n => n.HasCompleted);
54+
if (!_needs[i].HasCompleted)
55+
{
56+
return false;
57+
}
5558
}
56-
59+
60+
return true;
5761
}
5862
}
5963

src/Configuration/Variables.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ public void Add(ConcurrentDictionary<string, string>? variables)
6565

6666
AllVariables ??= new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
6767

68-
if (VariableList != null)
68+
if (VariableList != null && VariableList.Count > 0)
6969
{
70-
// Add the variables for the current object
71-
foreach (Variable variable in VariableList)
70+
// Add the variables for the current object using for loop to avoid enumerator allocation
71+
for (int i = 0; i < VariableList.Count; i++)
7272
{
73+
Variable variable = VariableList[i];
7374
if (variable.Name == null || variable.Value == null)
7475
{
7576
continue;
@@ -84,7 +85,7 @@ public void Add(ConcurrentDictionary<string, string>? variables)
8485
}
8586
}
8687

87-
if (variables != null)
88+
if (variables != null && variables.Count > 0)
8889
{
8990
// Add the variables passed into the method
9091
foreach (KeyValuePair<string, string> variable in variables)

src/Configuration/Watch.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -429,17 +429,19 @@ public void ProcessChange()
429429
return;
430430
}
431431

432-
// Peek at the queue to get correlation IDs for logging
433-
var queueSnapshot = _queue.ToArray();
434-
var firstCorrelationId = queueSnapshot.Length > 0 ? (Guid?)queueSnapshot[0].CorrelationId : null;
432+
// Peek at first item for correlation ID instead of copying entire queue
433+
Guid? firstCorrelationId = _queue.TryPeek(out ChangeInfo? firstChange) ? (Guid?)firstChange.CorrelationId : null;
435434
var correlationPrefix = firstCorrelationId.HasValue ? $"[{firstCorrelationId.Value}] " : "";
436435

437436
// Guard expensive DEBUG logging to avoid string allocations
438437
if (Logger.LogLevel <= LogLevel.DEBUG)
439438
{
439+
// Cache queue count to avoid repeated property access
440+
int queueCount = _queue.Count;
441+
440442
// Include first correlation ID in watch-level logs for context
441443
Logger.WriteLine(
442-
$"{correlationPrefix}{IdLogString}: ProcessChange started. CanRun: {CanRun}, IsRunning: {IsRunning}, Queue count: {_queue.Count}. (Watch.ProcessChange)",
444+
$"{correlationPrefix}{IdLogString}: ProcessChange started. CanRun: {CanRun}, IsRunning: {IsRunning}, Queue count: {queueCount}. (Watch.ProcessChange)",
443445
LogLevel.DEBUG);
444446

445447
// Detailed dependency information
@@ -494,9 +496,12 @@ public void ProcessChange()
494496

495497
if (Logger.LogLevel <= LogLevel.DEBUG)
496498
{
499+
// Cache remaining count
500+
int remainingCount = _queue.Count;
501+
497502
// Change-specific log WITH correlation ID
498503
Logger.WriteLine(
499-
$"[{change.CorrelationId}] {IdLogString}: Processing item {processedCount + 1} of current batch. Remaining in queue: {_queue.Count}. (Watch.ProcessChange)",
504+
$"[{change.CorrelationId}] {IdLogString}: Processing item {processedCount + 1} of current batch. Remaining in queue: {remainingCount}. (Watch.ProcessChange)",
500505
LogLevel.DEBUG);
501506
}
502507

src/Log/Message.cs

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,36 +26,15 @@ public class Message
2626
public long Ticks { get; }
2727

2828
/// <summary>
29-
/// Gets the string representation of the log level.
29+
/// Gets the string representation of the log level (cached).
3030
/// </summary>
31-
public string LevelString
32-
{
33-
get
34-
{
35-
return Level switch
36-
{
37-
LogLevel.DEBUG => "DEBUG",
38-
LogLevel.WARNING => "WARN ",
39-
LogLevel.ERROR => "ERROR",
40-
LogLevel.FATAL => "FATAL",
41-
_ => "INFO ",
42-
};
43-
}
44-
}
31+
public string LevelString { get; }
4532

4633
/// <summary>
47-
/// Gets the formatted timestamp with microsecond precision.
34+
/// Gets the formatted timestamp with microsecond precision (cached).
4835
/// Format: yyyy-MM-dd HH:mm:ss.ffffff
4936
/// </summary>
50-
public string FormattedTimestamp
51-
{
52-
get
53-
{
54-
// Calculate microseconds from ticks (10 ticks = 1 microsecond)
55-
long microseconds = (Ticks % TimeSpan.TicksPerMillisecond) / 10;
56-
return $"{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{microseconds:D3}";
57-
}
58-
}
37+
public string FormattedTimestamp { get; }
5938

6039
/// <summary>
6140
/// Initializes a <see cref="Message"/> class when provided with the
@@ -73,6 +52,20 @@ public Message(string value, LogLevel level)
7352
Level = level;
7453
Timestamp = DateTime.Now;
7554
Ticks = Timestamp.Ticks;
55+
56+
// Cache formatted strings to avoid repeated allocations
57+
LevelString = level switch
58+
{
59+
LogLevel.DEBUG => "DEBUG",
60+
LogLevel.WARNING => "WARN ",
61+
LogLevel.ERROR => "ERROR",
62+
LogLevel.FATAL => "FATAL",
63+
_ => "INFO ",
64+
};
65+
66+
// Calculate microseconds from ticks (10 ticks = 1 microsecond)
67+
long microseconds = (Ticks % TimeSpan.TicksPerMillisecond) / 10;
68+
FormattedTimestamp = $"{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{microseconds:D3}";
7669
}
7770
}
7871
}

src/Placeholder.cs

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -377,23 +377,26 @@ private string GetUrlEncodedValue(string placeholder, string value, string url)
377377
return null;
378378
}
379379

380+
// Pre-compute all replacement values
380381
string relativeFullPath = GetRelativeFullPath(fullPath, watchPath);
381382
string? relativePath = GetRelativePath(fullPath, watchPath);
382383
string? fileName = TEFS.File.GetName(fullPath, true);
383384
string? fileNameWithoutExtension = TEFS.File.GetName(fullPath, false);
384385
string? extension = TEFS.File.GetExtension(fullPath);
385386

386-
string replacedValue = value;
387-
replacedValue = replacedValue.Replace(PLACEHOLDERWATCHPATH, watchPath, StringComparison.OrdinalIgnoreCase);
388-
replacedValue = replacedValue.Replace(PLACEHOLDEREXACTPATH, fullPath, StringComparison.OrdinalIgnoreCase);
389-
replacedValue = replacedValue.Replace(PLACEHOLDERFULLPATH, relativeFullPath, StringComparison.OrdinalIgnoreCase);
390-
replacedValue = replacedValue.Replace(PLACEHOLDERPATH, relativePath, StringComparison.OrdinalIgnoreCase);
391-
replacedValue = replacedValue.Replace(PLACEHOLDERFILENAME, fileName, StringComparison.OrdinalIgnoreCase);
392-
replacedValue = replacedValue.Replace(PLACEHOLDERFILE, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase);
393-
replacedValue = replacedValue.Replace(PLACEHOLDEREXTENSION, extension, StringComparison.OrdinalIgnoreCase);
394-
395-
// If the changes include an old path, such as when a file/folder
396-
// is renamed, then replace those placeholders
387+
// Build replacement dictionary to avoid chained string allocations
388+
var replacements = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
389+
{
390+
[PLACEHOLDERWATCHPATH] = watchPath,
391+
[PLACEHOLDEREXACTPATH] = fullPath,
392+
[PLACEHOLDERFULLPATH] = relativeFullPath,
393+
[PLACEHOLDERPATH] = relativePath,
394+
[PLACEHOLDERFILENAME] = fileName,
395+
[PLACEHOLDERFILE] = fileNameWithoutExtension,
396+
[PLACEHOLDEREXTENSION] = extension
397+
};
398+
399+
// If the changes include an old path, add those replacements
397400
if (!string.IsNullOrWhiteSpace(oldPath))
398401
{
399402
string oldRelativeFullPath = GetRelativeFullPath(oldPath, watchPath);
@@ -402,12 +405,22 @@ private string GetUrlEncodedValue(string placeholder, string value, string url)
402405
string? oldFileNameWithoutExtension = TEFS.File.GetName(oldPath, false);
403406
string? oldExtension = TEFS.File.GetExtension(oldPath);
404407

405-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDEXACTPATH, oldPath, StringComparison.OrdinalIgnoreCase);
406-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDFULLPATH, oldRelativeFullPath, StringComparison.OrdinalIgnoreCase);
407-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDPATH, oldRelativePath, StringComparison.OrdinalIgnoreCase);
408-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDFILENAME, oldFileName, StringComparison.OrdinalIgnoreCase);
409-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDFILE, oldFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase);
410-
replacedValue = replacedValue.Replace(PLACEHOLDEROLDEXTENSION, oldExtension, StringComparison.OrdinalIgnoreCase);
408+
replacements[PLACEHOLDEROLDEXACTPATH] = oldPath;
409+
replacements[PLACEHOLDEROLDFULLPATH] = oldRelativeFullPath;
410+
replacements[PLACEHOLDEROLDPATH] = oldRelativePath;
411+
replacements[PLACEHOLDEROLDFILENAME] = oldFileName;
412+
replacements[PLACEHOLDEROLDFILE] = oldFileNameWithoutExtension;
413+
replacements[PLACEHOLDEROLDEXTENSION] = oldExtension;
414+
}
415+
416+
// Perform all replacements efficiently
417+
string replacedValue = value;
418+
foreach (var kvp in replacements)
419+
{
420+
if (kvp.Value != null)
421+
{
422+
replacedValue = replacedValue.Replace(kvp.Key, kvp.Value, StringComparison.OrdinalIgnoreCase);
423+
}
411424
}
412425

413426
return replacedValue;
@@ -445,12 +458,12 @@ private string GetUrlEncodedValue(string placeholder, string value, string url)
445458
{
446459
// Loop through each of the matches so the placeholder can
447460
// be replaced with the actual date values
448-
foreach (Match match in matches.Cast<Match>())
461+
// Use indexed access instead of Cast<Match>() to avoid LINQ allocation
462+
for (int i = 0; i < matches.Count; i++)
449463
{
464+
Match match = matches[i];
450465

451-
// Store the date type (createddate, modifieddate,
452-
// or currentdate) and change it to lowercase so it can
453-
// be easily compared later
466+
// Store the date type and convert to lowercase once
454467
string type = match.Groups["type"].Value.ToLower(CultureInfo.CurrentCulture);
455468
// Store the specified date format
456469
string format = match.Groups["format"].Value;

0 commit comments

Comments
 (0)