December 11, 2022

Json Parsing Tests - ExpandoObject or Json strings

Setting Up Jason Parsing Tests Using Dynamic Objects Or Strings

[Test]
public void SomeTestUsingExpandoObject()
{
  // arrange
  const string getItemEventDataValue = @"{
      ""_PrimaryId"": ""F:\\dasda\\ToMO-005775.json"",
      ""_SecondaryId"": ""123456"",
      ""RetrievalResult"": ""Processed Successfully""
    }";
  dynamic indexItemRaw = new ExpandoObject();
  indexItemRaw.id = sourceIndexItemId;
  indexItemRaw.version = sourceIndexItemVersion.ToString(CultureInfo.InvariantCulture);
  dynamic source = new ExpandoObject();
  source.getItemEventData = getItemEventDataValue;
  indexItemRaw.source = source;

  // act
  IEnumerable<ClaimDto> claimsEnum = someMapper.Map(JObject.FromObject(indexItemRaw));
  var claimDtos = claimsEnum.ToList();
  
  //assert
  ...
}

[Test]
public void SomeTestUsingJSonStringOnly()
{
  //How to set up unit tests using strings to set up the data, instead of dynamic objects.
  //Sometimes it can be simpler, especially if you retrieve the raw data from a run
  //Here is the above test, arranged using a string as an example
  //In this case I thought it was more complicated to set everything up as a string
  //(because one of the json string object entries (the "getItemEventData") was itself a json string)
  // arrange
  string rawJToken = @"
  {
    ""id"": ""{sourceIndexItemId}"",
    ""version"": ""1.0"",
    ""source"": {
      ""getItemEventData"": ""{
         \""PrimaryId\"": \""F:\\\\dasda\\\\ToMO-005775.json\"",
         \""SecondaryId\"": \""123456\""
         \""RetrievalResult\"": \""Processed Successfully\""
      }""
    }
  }";
  rawJToken = rawJToken.Replace("{sourceIndexItemId}", sourceIndexItemId);
  JToken jtoken = JToken.Parse(rawJToken);

  //act
  IEnumerable<ClaimDto> claimsEnum = someMapper.Map(jtoken);
  var claimDtos = claimsEnum.ToList();

  //assert
  ...
}

December 4, 2022

Plant UML

Quite often I need to examine some existing code to find a solution to a problem. Sometimes this requires going in detail about what calls what and when. However, usually the code your looking at has many calls to many other objects. We don't want the details of every single call, just the important ones. Here sequence diagrams are very useful to document this visually. I prefer to use PlantUML for this because it allows you to write the diagram as text, PlantUml takes care of spacing everything appropriately. You can just get your object name/type and the method called on it from your IDE.
PlantUML is found at this web address: https://www.plantuml.com/ .
Here is some good documentation on how to use it: https://crashedmind.github.io/PlantUMLHitchhikersGuide/ .
Here is an example of using Plant UML to create a sequence diagram in UML:

Here is the resulting image, click on it to open it full size:

Linqpad Notepad++ Hyperlink Extension

When searching through some files using LinqPad it can be useful to print out the results as a hyperlink that will open the file in Notepad++ at a particular line number.
public static class MyExtensions
{
    // Write custom extension methods here. They will be available to all queries.

    private const string notePadppPath = @"C:\Program Files\Tools\Notepad++\notepad++.exe";
    
    public static Hyperlinq CreateNotePadppHyperLink(this string filePath, int lineNumber)
    {
        var filelink = new Hyperlinq(() => Process.Start(notePadppPath, filePath + " -n" + lineNumber.ToString()), filePath);
        return filelink;
    }

}
Usage
string filePath = .... ;
filePath.CreateNotePadppHyperLink(lineNumber);

August 8, 2022

String Replace with StringComparison

I found this https://stackoverflow.com/questions/244531/is-there-an-alternative-to-string-replace-that-is-case-insensitive/13847351#comment31063745_244933 I added a few defensive checks.
public static string Replace(
    this string src,
    string oldValue,
    string newValue,
    StringComparison comparison)
{
    if (string.IsNullOrEmpty(oldValue))
        throw new ArgumentException("String cannot be of zero length or null.", "oldValue");
        
    if (string.IsNullOrEmpty(newValue)) // It can be an empty string though
        throw new ArgumentException("String cannot be null.", "newValue");

    // skip the loop entirely if oldValue and newValue are the same
    if (string.Compare(oldValue, newValue, comparison) == 0)
        return src;

    if (oldValue.Length > src.Length)
        return src;

    int index = src.IndexOf(oldValue, comparison);
    if (index == -1)
        return src;

    var sb = new StringBuilder(src.Length);

    int previousIndex = 0;

    while (index != -1)
    {
        sb.Append(src.Substring(previousIndex, index - previousIndex));
        sb.Append(newValue);
        index += oldValue.Length;

        previousIndex = index;
        index = src.IndexOf(oldValue, index, comparison);
    }

    sb.Append(src.Substring(previousIndex));

    return sb.ToString();
}

July 12, 2022

xUnit vs MSTest vs NUnit

There is a good comparison of the 3 unit test libraries:

https://xunit.net/docs/comparisons

June 6, 2022

EventAggregator in Javascript

I found an EventAggregator in Javascript here: https://davidwalsh.name/pubsub-javascript. It was nice and simple, but it was using a hidden javascript object to map the topic string to an array of listeners. I thought that a Map is more appropriate for this functionality so I changed the code to use one.

I do a lot of logging because this class tends to be the thing that fires all the domain events in a project, so logging here is very useful. I would like to convert it to Typescript some time. In my case the events were mostly sent to the Controller portion of MVC controllers which would decide if they needed to update their Models/Views


"use strict";

// Originally from here: https://davidwalsh.name/pubsub-javascript, 
// changed to use the Map class
// Allows objects to publish and subscribe to custom events/topics
// Changed to use a Map class rather than using an object
var eventAggregator = (function () {
    // maps a topic string to an array of subscriber/listener functions, 
    // each subscriber/listener function takes a single data object parameter 
    var _topicsMap = new Map();
    var _publishingEnabled = false;

    return {
        // topic is a string, listener is a function that takes a 
        // single data object parameter
        Subscribe: function (topic, listener) { 
            // Create the topic's object if not yet created, 
            // the key is the topic string, the value is an empty array (of listeners)
            if (!_topicsMap.has(topic)) _topicsMap.set(topic, []);

            // Add the listener to the array
            var index = _topicsMap.get(topic).push(listener) - 1;

            // Provide handle back for removal of topic
            return {
                Dispose: function () {
                    delete _topicsMap.get(topic)[index];
                }
            };
        },

        Enabled: function (isEnabled) {
            if (_publishingEnabled != isEnabled) {
                _publishingEnabled = isEnabled;
                let isEnabledStr = _publishingEnabled ? "enabled" : "disabled";
                console.log("Events publishing is " + isEnabledStr);
            }
        },

        Publish: function (topic, data) {
            // If publishing is disabled, or the topic doesn't exist,
            // or there's no listeners in the array, just leave
            if (!_publishingEnabled) {
                console.log("'" + topic + "' Events: Publishing is disabled");
                return;
            }
            else if (!_topicsMap.has(topic)) {
                console.warn("Events: Either the topic '" + topic + 
                "' doesn't exist, or there's no listeners in queue");
                return;
            }

            let dataSafe = (data) ? data : "";
            if (typeof dataSafe === 'string' || dataSafe instanceof String) {
                console.log("Events: event raised on topic: '"
                   + topic + "' with data '" + dataSafe + "'");
            } else {
               console.log("Events: event raised on topic: '"
                 + topic + "' with '" + JSON.stringify({ data }) + "'");
            }
            // Cycle through topics array, invoking each listener function
            _topicsMap.get(topic).forEach(function (item) {
                item(data != undefined ? data : {});
            });
        }
    };
})();

May 5, 2022

IWebHostEnvironment MapPath Extension

Brings MapPath() to .NET Core

public static class IWebHostEnvironmentExtender
{
  public static string MapPath(
    this IWebHostEnvironment self, string path)
  {
    var res = path;
    var ix = path.IndexOf('~');
    if (ix == 0)
    {
      res = path.Replace("~", self.WebRootPath);
    }
    return res;
  }
}

FileSystemWatcher Extensions

We can use extension classes to allow React to be used with other FileSystemWatcher usage cases:
public static class FileSystemWatcherExtender
{
  // Returns an IObservable, which is effectively a publisher
  // which can be subscribed to and disposed of when finished with
  public static IObservable<FileSystemEventArgs> 
    GetFileChangedPublisher(this FileSystemWatcher fileSystemWatcher)
  {
      Debug.Assert(fileSystemWatcher != null);
      var result = Observable
        .FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
            ev => fileSystemWatcher.Changed += ev,
            ev => fileSystemWatcher.Changed -= ev)
        .Select(ev => ev.EventArgs);
      return result;
  }

  // Use this to subscribe to FileSystemWatcher errors
  public static IObservable<ErrorEventArgs> 
    GetErrorPublisher(this FileSystemWatcher fileSystemWatcher)
  {
    Debug.Assert(fileSystemWatcher != null);
    return Observable
      .FromEventPattern<ErrorEventHandler, ErrorEventArgs>(
            ev => fileSystemWatcher.Error += ev,
            ev => fileSystemWatcher.Error -= ev)
      .Select(x => x.EventArgs);
  }
}
And to use it
FileSystemWatcher filesystemwatcher = BuildMyFileSystemWatcher();
...
// Subscribed to filesystem watcher changes
// where "onChanged" is a Func<FileSystemEventArgs>
IDisposable subscription = fileSystemWatcher.GetFileChangedPublisher().Subscribe(onChanged);   
...
subscription.Dispose(); // Finish with our subscription
...

May 4, 2022

A File Change Monitor Class in C#

Wrote this File changes monitor service to monitor 1 or more files in a particular directory for file write accesses.
public class FileChangedEvent
{
    public FileChangedEvent(string fullPath, string fileName, 
      WatcherChangeTypes changeType)
    {
        FullPath = fullPath;
        FileName = fileName;
        ChangeType = changeType;
    }

    // Full file path and name
    public string FullPath { get; private set; }
    
    // File name only
    public string FileName { get; private set; }
    
    // The type of change
    public WatcherChangeTypes ChangeType { get; private set; }
}

public interface IFileChangeMonitor
{
    void Dispose();
    string DirectoryPath { get; }
    void Start();
    public bool IsActive { get; }
    void Stop();
    IDisposable SubscribeOnChanged(string fileName, 
      Action<FileChangedEvent> onChanged, double throttle_ms = 100);
    IDisposable SubscribeOnError(Action<ErrorEventArgs> onError);
}

public class FileChangeMonitor : IDisposable, IFileChangeMonitor
{
    // https://weblogs.asp.net/ashben/31773 
    private readonly FileSystemWatcher _fileSystemWatcher; 
    private readonly DirectoryInfo _directory; // directory being monitored
    private const string DefaultFileFilter = @"*.json";

    // TODO: add NotifyFilter parameter
    public FileChangeMonitor(DirectoryInfo dir, 
      string defaultFileFilter = DefaultFileFilter)
    {
        //const int MAX_NUM_FILES_TO_MONITOR = 10;
        //const int AVG_FILE_NAME_LENGTH = 30;
        //const int FOUR_KB = 1024 * 4;
        Debug.Assert(dir != null);
        Debug.Assert(dir.Exists == true);

        _directory = new DirectoryInfo(dir.FullName);
        //int bufferSize = ((MAX_NUM_FILES_TO_MONITOR * 
        // (16 + AVG_FILE_NAME_LENGTH * 2) % FOUR_KB) + 1) * FOUR_KB;
        _fileSystemWatcher = new FileSystemWatcher(_directory.FullName)
        {
            //InternalBufferSize = bufferSize, default is enough
            NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite,
            Filter = defaultFileFilter,
            IncludeSubdirectories = false
        };
    }

    public string DirectoryPath => _directory?.FullName;

    public IDisposable SubscribeOnChanged(string fileName, 
      Action<FileChangedEvent> onChanged, 
      double throttle_ms = 200)
    {
        Debug.Assert(_directory?.Exists == true);
        Trace.WriteLine($"*** Subscribing for changes to 
  {Path.Combine(DirectoryPath, fileName)}");
        return GetFileChangedPublisher(fileName, throttle_ms)
          .Subscribe(onChanged);
    }

    public void Start()
    {
        Debug.Assert(_directory?.Exists == true);
        _fileSystemWatcher.EnableRaisingEvents = true;
    }

    public bool IsActive => _fileSystemWatcher.EnableRaisingEvents;

    public void Stop()
    {
        Debug.Assert(_directory?.Exists == true);
        _fileSystemWatcher.EnableRaisingEvents = false;
    }

    public void Dispose()
    {
        if (IsActive)
            Stop();
        GC.SuppressFinalize(this);
        _fileSystemWatcher.Dispose();
    }

    // Returns an IObservable, which is effectively a publisher which
    // can be subscribed to and disposed of when finished with
    private IObservable<FileChangedEvent> GetFileChangedPublisher(
      string targetFileName, 
      double throttle_ms = 200)
    {
        Debug.Assert(_directory?.Exists == true);

        IObservable<EventPattern<FileSystemEventArgs>> fswCreated = 
          Observable.FromEventPattern<FileSystemEventArgs>(_fileSystemWatcher, "Changed");

        return fswCreated.
               Where((EventPattern<FileSystemEventArgs> ev) => ev.EventArgs.Name == targetFileName).
               // Rx Extension Sample() will only allow the last event within a time
               // interval to pass through. This effectively throttles events
               Sample(TimeSpan.FromMilliseconds(throttle_ms)).
               Select(ev => new FileChangedEvent(ev.EventArgs.FullPath, ev.EventArgs.Name, ev.EventArgs.ChangeType)); 
    }

    private IObservable<ErrorEventArgs> GetErrorPublisher()
    {
        Debug.Assert(_directory?.Exists == true);

        return Observable
          .FromEventPattern<ErrorEventHandler, ErrorEventArgs>(
                   ev => _fileSystemWatcher.Error += ev,
                   ev => _fileSystemWatcher.Error -= ev)
          .Select(x => x.EventArgs);
    }

    public IDisposable SubscribeOnError(Action<ErrorEventArgs> onError)
    {
        Debug.Assert(_directory?.Exists == true);
        return GetErrorPublisher().Subscribe(onError);
    }
}

The thing is it is an example of how to integrate React with a file system watcher or an existing class that publishes events using standard C# events. Here is an example of how to use the service

DirectoryInfo dir = new DirectoryInfo("\\Some\Directory");
FileChangeMonitor datafileMonitor = new FileChangeMonitor(dir);
datafileMonitor.Start();
...
// Subscribed to just 1 file but we could subscribe to multiple in the same directory
IDisposable subscription = datafileMonitor.SubscribeOnChanged(
    "FileToMonitor.json",
    fce => DoSomethingWith(fce));   
...
subscription.Dispose(); // Finish with our subscription to a specific file's changes
...
datafileMonitor.Stop(); // Optionally we could just call just Dispose()
datafileMonitor.Dispose();

We could change the class so that the type of changes monitored could be passed in through the class constructor (the NotifyFilter enumeration). Also, an option to change the internal buffer size could be required.

May 1, 2022

Logging Assertions in ASP .Net Core

Sometimes it useful to extend the logging system so that you can log assertions/code contracts. Simple logger add-on so that assertions/code contracts can be logged in ASP.Net:
using System.Runtime.CompilerServices;

public static class ILoggerAssertionExtender
{
  public static bool Assert(this ILogger logger, 
    Func<bool> predicate, 
    Func<string> message)
  {
      bool assertion = predicate();
      if (!assertion)
      {
          logger.LogError("Code contract/assertion failed :" + message());
      }
      // The assertion outcome is returned so the user can react to it (throw an exception, 
      // insert a DebuggerBreak(), ...) after the logging is done.
      return assertion; 
  }
  
  // This one adds Caller attributes to get more details
  public static bool CallerAssert(this ILogger logger, 
    Func<bool> predicate,
    Func<string> message,
    [CallerMemberName] string member = "", 
    [CallerFilePath] string file = "", 
    [CallerLineNumber] int line = -1)
  {
      bool assertion = predicate();
      if (!assertion)
      {
          logger.LogError($"Code contract/assertion failed in Member {member}, File {file}, Line {line}: " + message());
      }
      // The assertion outcome is returned so the user can react to it (throw an exception, 
      // insert a DebuggerBreak(), ...) after the logging is done.
      return assertion; 
  }
}

April 28, 2022

Sample HostApplicationLifetime Events Class for ASP .Net Core

There are some environments where you are using an underlying technology to assist you, for example, WPF, Win Forms, and ASP.NET. It is useful sometimes in these environments to be able to initialise application code just after the underlying technology has been initialised, and have some termination/finalisation code invoked just before the underlying technology is unwound.

In my case, I had to call a service that started a FileSystemWatcher when the web hosting service started and stopped it when the web hosting service was about to end. These events are very important in the hosts lifetime, so I added logging to record them. I also used my own application configuration file which was beyond that provided by the ASP.Net Core services, but the location of which was set within it.

Here is a class to do this in .NET Core:
/// <summary>
/// This class tells you when the Web Application 
/// Starts (just prior to listening for web requests) 
/// and Stops and has Stopped, so you can start things up 
/// and shut them down at the end if they fit that kind of pattern
/// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.0#ihostapplicationlifetime
/// </summary>
internal class WebApplicationLifetimeEvents : IHostedService
{
    private readonly ILogger _logger;
    private readonly IHostApplicationLifetime _appLifetime;
    private readonly IWebHostEnvironment _webhostEnvironment;


    public WebApplicationLifetimeEvents(
        ILogger<WebApplicationLifetimeEvents> logger,
        IHostApplicationLifetime appLifetime,
        IWebHostEnvironment webhostEnvironment,
        )
    {
        
        _logger = logger;
        _appLifetime = appLifetime;
        _webhostEnvironment = webhostEnvironment;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _appLifetime.ApplicationStarted.Register(OnStarted);
        _appLifetime.ApplicationStopping.Register(OnStopping);
        _appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("OnStarted has been called.");
         // Perform post-startup activities here:
        try
        {

        }
        catch (Exception ex)
        {
            _logger.LogError($"Exception in OnStarted {ex}");
            throw;
        }
    }

    private void OnStopping()
    {
        _logger.LogInformation("OnStopping has been called.");

        // Perform on-stopping activities here
    }

    private void OnStopped()
    {
        _logger.LogInformation("OnStopped has been called.");

        // Perform post-stopped activities here
    }
}
Then to use the service:
services.AddSingleton<IHostedService, WebApplicationLifetimeEvents> ();