August 26, 2021

Exposing C# Events using Reactive Extensions

There is a way to convert existing C# events to an IObservable<>. There is a good article here on this

public class SimpleEventSource
{
  public event EventHandler<MyEventArgs> SimpleEvent;
     
  // Normally this would be private (event is triggered internally) 
  // but for test purposes we make it public
  public void FireSimpleEvent(MyEvent myevent)
  {
    EventHandler<MyEventArgs> tmp = SimpleEvent;
    if (tmp != null)
    {
      tmp(this, new MyEventArgs(myEvent));
    }
  }   
 
  // This returns an IObservable (effectively an event pulisher)
  public IObservable<EventPattern<MyEventArgs>> MyEventPublisher
  {
    get
    {       // To consume SimpleEvent as an IObservable:
        IObservable<EventPattern<MyEventArgs>> eventAsObservable = Observable.
            FromEventPattern<MyEventArgs>(
              ev => SimpleEvent += ev,
              ev => SimpleEvent -= ev);
        return eventAsObservable;
    }
  }

}

Note that MyEventArgs is something derived from EventArgs that allows access to the "MyEvent" instance, see below. Here is a tester:

public void TestSimpleEventSource()
{
    var ses = new SimpleEventSource();
    ses.FireSimpleEvent(new MyEvent("1st event")); // This one is missed

    using (var subscription = ses.MyEventPublisher.Subscribe(
        ev => Console.WriteLine(ev.EventArgs.MyEvent.Text + " fired")))
    {
        ses.FireSimpleEvent(new MyEvent("2nd event"));
        ses.FireSimpleEvent(new MyEvent("3rd event"));               
    }

    Console.WriteLine("Subscription cancelled");
}
and the output
2nd event fired
3rd event fired
Subscription cancelled

Replacing C# Events using Reactive Extensions

Lets take this one step further, and completely replace the C# event with a reactive style event publisher

internal class SimpleEventSource
{
    private Subject<MyEvent> _myEventSubject = new Subject<MyEvent>();

    public IObservable<MyEvent> MyEventPublisher => this._myEventSubject.AsObservable();

    public void FireSimpleEvent(MyEvent myEvent) => this._myEventSubject.OnNext(myEvent);
}
And the same test but using this class:
public void TestSimpleEventSource2()
{
    var ses = new SimpleEventSource2();
    ses.FireSimpleEvent(new MyEvent("1st event"));

    using (var subscription = ses.MyEventPublisher.Subscribe(
        ev => Console.WriteLine(ev.Text + " fired")))
    {
        ses.FireSimpleEvent(new MyEvent("2nd event"));
        ses.FireSimpleEvent(new MyEvent("3rd event"));
    }

    Console.WriteLine("Subscription cancelled");
}

Here is the code for the simple event classes if it makes things clearer

public class MyEvent
{
    public MyEvent(string text)
    {
        Text = text;
    }
    public string Text { get; private set; }
}

public class MyEventArgs : EventArgs
{
    public MyEventArgs(MyEvent myEvent)
        : base()
    {
        MyEvent = myEvent;
    }
    public MyEvent MyEvent { get; private set; }
}

August 20, 2021

Stopwatch Extension

Some simple code to use a Stopwatch class to measure how long a piece of code takes to run. With this extension you can forget about the Stopwatch syntax:

namespace Utilities.StopwatchExtensions
{
  public static class StopwatchExtender
  {
    public static TimeSpan MeasureRunTime(this Action codeToRun)
    {
        var stopWatch = Stopwatch.StartNew();
        codeToRun();
        stopWatch.Stop();
        return stopWatch.Elapsed;
    }
  }
}

From .NET 7.0 you can use this form:

namespace Utilities.StopwatchExtensions
{
  public static class StopwatchExtender
  {
    public static TimeSpan MeasureRunTime(this Action codeToRun)
    {
        long startTime = Stopwatch.GetTimestamp();;
        codeToRun();
        TimeSpan elapsedTime = Stopwatch.GetElapsedTime(startTime);
    }
  }
}
Or put them together
namespace Utilities.StopwatchExtensions
{
  /// <summary>
  /// Stopwatch extension tomeasure the time to perform an Action
  /// </summary>
  /// <example>
  /// <code>
  ///  Action work = () => list = someDb.Get().ToList();
  ///  var timeTakenMs = work.MeasureRunTime().TotalMilliseconds;
  /// </code>
  /// </example>
  public static class StopwatchExtender
  {
    public static TimeSpan MeasureRunTime(this Action codeToRun)
      {
      TimeSpan elapsedTime = TimeSpan.Zero;
#if NET7_0_OR_GREATER
      var startTime = Stopwatch.GetTimestamp();
      codeToRun();
      elapsedTime = Stopwatch.GetElapsedTime(startTime);
#else
      var sw = Stopwatch.StartNew();
      codeToRun();
      sw.Stop();
      elapsedTime = sw.Elapsed;
#endif
      return elapsedTime;
    }
  }
}
Remember to wrap the extension in a specific isolating namespace otherwise the intelli-editor in Visual Studio will get polluted with unused extension methods. To use:
Action codeToRun = () => { /* code whose execution time needs measuring */};
var runTimeMs = codeToRun.MeasureRunTime().TotalMilliseconds;
Trace.WriteLine("*** Time to complete task: " + runTimeMs + " ms");

using (var process = Process.GetCurrentProcess())
{
    Trace.WriteLine($"*** Execution time: {runTimeMs.ToString()} ms");
    Trace.WriteLine($"*** Gen-0: {GC.CollectionCount(0).ToString()}");
    Trace.WriteLine($"*** Gen-1: {GC.CollectionCount(1).ToString()}");
    Trace.WriteLine($"*** Gen-2: {GC.CollectionCount(2).ToString()}");
    Trace.WriteLine($"*** Peak WrkSet: {process.PeakWorkingSet64.ToString("n0")}");
}

August 19, 2021

C# Event Aggregators

Did some research into event aggregators. They are a great way to allow events to be fired betwen classes without the classes being tightly coupled, in other words without either class having to know about the other. Standard C# events make the classes tighly coupled, the listener needs to know about the firer. Sometimes with standard c# events, an event will need to be transmitted through an intermediate sequence of objects just to reach the target recipient, this is very cumbersome and laborious. Event aggregators completely decouple the event publishers from the event subscribers as long as both subscriber and publisher objects can access the event aggregator their is no need for intermediate objects to relay the events

Would make an excellent start for a simulator which has both hardware and software events occuring, an event aggregator could be used to input events to the system. As it is completely decoupled from the system, it would be easy to emulate firing these software events say from a GUI for emulation purposes, and also easy to connect to real hardware to populate those same software events.

I found several links that use a Reactive Extensions to implement an Event Aggregator. Using the Reactive Extensions the code to implement an aggregator is very small and compact.

  1. https://mikebridge.github.io/post/csharp-domain-event-aggregator
It is also a great example in the usage of Reactive Extensions. Currently I have combined them into this form:

using System.Reactive.Linq; // You'll need the Reactive nuget package
using System.Reactive.Subjects;
...

public interface IRxEventAggregator : IEventAggregator, IDisposable
{
    /// <summary>
    ///  Use this method to apply RX (LINQ style) expressions on
    ///  the events before subscribing
    /// </summary>
    /// <typeparam name="TEvent"></typeparam>
    /// <returns></returns>
    IObservable<TEvent> GetEventsObservable<TEvent>();
}

public interface IEventAggregator
{
    IDisposable Subscribe<T>(Action<T> action);
    void Publish<T>(T @event);
}


/// <summary>
/// An EventAggregator that is based on reactive extensions and supports
/// </summary>
public class RxEventAggregator
    : IRxEventAggregator
{
    readonly Subject<object> _subject = new Subject<object>();

    // Use this method to use RX (LINQ style) expressions
    // on the events before subscribing
    public IObservable<TEvent> GetEventsObservable<TEvent>()
    {
        return _subject.OfType<TEvent>().
            AsObservable();
    }

    public IDisposable Subscribe<T>(Action<T> action)
    {
        return GetEventsObservable<T>().
            Subscribe(action);
    }

    public void Publish<TEvent>(TEvent sampleEvent)
    {
        _subject.OnNext(sampleEvent);
    }

    #region IDisposable

... // See below

    #endregion
}

Generally an Event Aggregator lives throughout the life of your software so it really does not need disposing of. You could remove the Dispose code completely. For that reason I list it here just for reference:

    #region IDisposable

    bool _disposed;

    ~RxEventAggregator()
    {
        Dispose(false);
         // Useful for detecting memory leaks
        Debug.Assert(false, nameof(RxEventAggregator) + 
            " was not disposed of");
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); 
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        _subject.Dispose();
        _disposed = true;
    }

    #endregion

Here's another event aggregator:

// When there are a lot of events with lots of subscribers
public class RxEventAggregator
: IRxEventAggregator
{
    private volatile ConcurrentDictionary<Type, object> _subjects
        = new ConcurrentDictionary<Type, object>();

    // Use this method to use RX (LINQ style) expressions
    // on the events before subscribing
    public IObservable<TEvent> GetEventsObservable<TEvent>()
    {
        Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
        var subject =
            (ISubject<TEvent>)_subjects.GetOrAdd(typeof(TEvent),
                        _ => new Subject<TEvent>());
        return subject.AsObservable();
    }

    public IDisposable Subscribe<T>(Action<T> action)
    {
        Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
        return GetEventsObservable<T>().
            Subscribe(action);
    }

    public void Publish<TEvent>(TEvent sampleEvent)
    {
        Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
        if (_subjects.TryGetValue(typeof(TEvent), out var subject))
        {
            ((ISubject<TEvent>)subject)
                .OnNext(sampleEvent);
        }
    }

    // If your Aggregator is a life (of the application) long one you can simply not bother with disposing of it
    #region IDisposable 

    bool _disposed;

    ~RxEventAggregator2()
    {
        Debug.Assert(false, nameof(RxEventAggregator2) + " was not disposed of"); // Useful for detecting memory leaks
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);  // no need to call finalizer now
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        _disposed = true;
        // Make them unaccessible
        var hiddenSubjects = Interlocked.Exchange(ref _subjects, new ConcurrentDictionary<Type, object>());
        // Then dispose of them
        foreach (var subject in hiddenSubjects.Values.Cast<IDisposable>())
        {
            subject.Dispose();
        }

    }

    #endregion
}

August 12, 2021

New Project File Format (VS 2017/2019)

There is a new alternative project file format from Visual Stuidio 2017 onwards. Checkout this description here

Here is an example of a WPF project file
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

Note the "TargetFramework" of ".net5.0-windows" is required for WinForms, WPF applications. These are supported in .NET 5.0 but only as windows applications.

Here is a library project that will build to multiple .Net versions both core and frameworks

<Project Sdk="Microsoft.NET.Sdk">
  <!-- Main build info -->
  <PropertyGroup>
    <TargetFrameworks>net48;net5.0</TargetFrameworks>
    <PackageId>MyProject</PackageId>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <SignAssembly>false</SignAssembly>
  </PropertyGroup>
  
  <!-- Assembly Info -->
  <PropertyGroup>
    <AssemblyVersion>3.7.2.3</AssemblyVersion>
    <Description>An implementation of MyProject.</Description>
    <Authors>Joe Blogz</Authors>
    <Copyright>2019 Magicsoft Corp.</Copyright>
    <Company>Magicsoft Corp.</Company>
    <PackageTags>Subject1;Subject2</PackageTags>
    <PackageProjectUrl>https://myproject.com/url</PackageProjectUrl>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <PackageReleaseNotes>-</PackageReleaseNotes>
  </PropertyGroup>
  
  <!-- Repo Info -->
  <PropertyGroup>
    <RepositoryUrl>https://github.com/MyProject</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
  </PropertyGroup>

  <!-- Remove AssemblyInfo.cs from build (in Properties dir.) -->
  <!-- it is now in the project file -->
  <ItemGroup>
    <Compile Remove="Properties\**" />
    <EmbeddedResource Remove="Properties\**" />
    <None Remove="Properties\**" />
  </ItemGroup>

  <!-- NUnit packages go here -->
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="System.ValueTuple" Version="4.5.0" />
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
    <Compile Remove="MyProject (net48)\**" />
  </ItemGroup>
</Project>

For multiple frame work targets <TargetFramework>..<TargetFramework> is replaced with <TargetFrameworks>...<TargetFrameworks> Note that .Net farmeworks use the style netnn where nn is the version but .Net version 5.0 onwards (which are .NET Core frameworks) use the form netn.n where n.n represesnt the .NET version. See https://docs.microsoft.com/en-us/dotnet/standard/frameworks for all possible entries

Note that the version info can be removed from the Assembly.cs and placed into the project file now. Referencing Nuget packages is also simplified.

Projects can be migrated with the help of the CsprojToVs2017 tool. Don't bother downloading the source and building it yourself, instead install it quickly using the dotnet install tool. This is described in the Quick Start Section. It will do most of the work of porting projects accross from the old format for you.

Some parts of a project file can be conditional, this feature is described here. For example in the project above I had to exclude some files that would only build in .NET Framework 4.8 or less. I placed these files in a subdirectory "CommonCore (net48)" and exclude them from the .NET 5.0 build.

Task.IsCompletedSuccessfully Property Extension

Task in all versions of the the .Net framework and .Net Standard 2.0 has a useful property missing. From .Net Standard 2.1 and through all versions of .Net Core the "IsCompletedSuccessfully" property is available. This will tell the use that the task completed without throwing an exeception or because it was cancelled. Well we can use an extension to make up for this missing property.

namespace System.Threading.Tasks
{
  public static class TaskIsCompletedSuccessfullyExtension
  {
    public static bool IsCompletedSuccessfully(this Task task)
    {
      var value = task.IsCompleted && !(task.IsCanceled || task.IsFaulted);
      return value;
    }
  }
}
and to use:
using System.Threading.Tasks;
...

Task myTask = ...

// Note it is defined not as a property but as a method
if (myTask.IsCompletedSuccessfully())
{
...
}

Using CallMemberAttribute in an INotifyPropertyChanged Base Class

Here is an sample base clase that makes implementing INotifyPropertyChanged easy. It is also a nice example of using templated methods on a class in C# without making the whole class templated.
using System.Runtime.CompilerServices;
...

public abstract class NotifyProperyChangedBase : INotifyPropertyChanged
{
    /// <summary>
    /// Multicast event for property change notifications.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    ///     Checks if a property already matches a desired value.
    ///     Sets the property and
    ///     notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with 
    /// both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">
    ///     Name of the property used to notify listeners.  This
    ///     value is optional and can be provided automatically 
    ///     when invoked from compilers that support CallerMemberName.
    /// </param>
    /// <returns>
    ///     True if the value was changed, false if the existing
    ///     value matched the desired value.
    /// </returns>
    protected bool SetProperty<T>(
        ref T storage, 
        T value, 
        [CallerMemberName] String propertyName = null)
    {
        bool valueChanged = !EqualityComparer<T>.Default.Equals(storage, value);
        if (valueChanged)
        {
            storage = value;
            OnPropertyChanged(propertyName);
            valueChanged = true;
        }
        return valueChanged;
    }

    /// <summary>
    ///     Checks if a property already matches a desired value.
    ///     Sets the property and notifies listeners only when 
    ///     necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property 
    /// with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="equalityComparer">
    ///     Specify the equality comparer.
    ///     For example if it is a non-case sensitive string you could use
    ///     StringComparer.CurrentCultureIgnoreCase</param>
    /// <param name="propertyName">
    ///     Name of the property used to notify listeners.  This
    ///     value is optional and can be provided automatically
    ///     when invoked from compilers that support CallerMemberName.
    /// </param>
    /// <returns>
    ///     True if the value was changed, false if the existing
    ///     value matched the desired value.
    /// </returns>
    protected bool SetProperty<T>(
        ref T storage, 
        T value, 
        IEqualityComparer<T> equalityComparer,
        [CallerMemberName] String propertyName = null)
    {
        bool valueChanged = !equalityComparer.Equals(storage, value);

        if (valueChanged)
        {
            storage = value;
            OnPropertyChanged(propertyName);
            valueChanged = true;
        }

        return valueChanged;
    }

    /// <summary>
    ///     Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">
    ///     Name of the property used to notify listeners. This
    ///     value is optional and can be provided automatically
    ///     when invoked from compilers that support 
    ///     <see cref="CallerMemberNameAttribute" />.
    /// </param>
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
and here is an example usage inside a ViewModel class:
private string _notifyIconToolTipText = "";

public string NotifyIconToolTipText
{
    get
    {
        return _notifyIconToolTipText;               
    }
    set
    {
        string newValue = value ?? "";
        this.SetProperty(ref _notifyIconToolTipText, newValue);
    }
}

July 31, 2021

System Defined Delegates

Action - When you want a delegate for a member/method that may or may not take parameters (up to 16 parameters max, none as the minimum) and does not return anything. The pattern is Action<T1, ..., T16>, so a member/method with 0 to 16 parameters that has no return value.

Func - When you want a delegate for a member/method that may or may not take parameters (up to 16 parameters max, none as the minimum) but that always returns a result. The pattern is Func<T1, ..., T16, TResult>, so a method with 0 to 16 parameters that returns a TResult.

Predicate - Defined as public delegate bool Predicate<in T>(T myObject);. A special delegate used by several methods of the Array and List<T> classes to search for elements in the collection. In other cases use a Func<T, bool>

My Take on SOLID (WIP)

S - Single-responsiblity Principle
A software entity (classes, modules, methods, functions, ...) should have only one responsibility, if you need to change a software entity for new requirements then this is a pointer towards adding new functionality and should be avoided. The new fucntionality (or responsibility) should be placed on another entity or a new one.
The aim is to lean towards small simple classes, avoid monster do everything classes at all costs. Smaller simpler classes have (at least) 3 advantages, they are simpler to code, simpler (especially for someone else) to understand, and simpler to test. This approach does have the disadvantage that a project becomes swamped with classes each with their own very specific responsibility and sometimes you cannot see the woods for the trees. For this reason, it is more important in a class definition to define the exact responsibility succinctly in the name of the class or where there is any possible doubt, expressed definitively in the class comments.

O - Open for extension/closed for changes
Code should be extended but not modified, especially released code. The trouble is that the more a piece of code changes (especially released code), the more likely bugs will be introduced and that existing tested code will require re-testing. Also this will likely break existing unit tests, or require new unit tests to validate the changes.
Very important when releasing a hotfix. During a hotfix you will break this rule but the trick is to do so in a minimal manner and where possible, change it in the next release so that the modification becomes an extension. Sometimes a hotfix unavoidably requires a lot of modifications in which case the hotfix will need to become an update.

Requires further comment: Surely if you take some code and extend it, say by adding a method to an existing class, you've also modified it.

L - Liskov substitution principle.
When you look at the definition of this it talks about being able to substitute instances of a base class with derived classes. However this was defined in the context of C++ where interfaces are not a language defined thing, you have to implement them as abstract base classes. I think that the definition should be redefined in terms of interfaces. If several classes implement an interface, then the behaviour of each interface implementation should be consistent. There should not be any returning of strange values on an interface member because in that instance that particular member does not really do anything or is not really appropriate. It should do what the client expects it to do.

I - Interfaces for client objects.
Expose your objects as interfaces or abstract classes for client objects to use, not as concrete objects. That way you can change the implementation without the client needing to change. Different client types should receive their own interface.

D - Depend on interfaces not concrete objects.
This follows on from the last principle, a client object should receive an interface when it needs to use an object, not the concrete object itself.

Requires further comment: This leads on to Dependency injection as a tool to help stick to this principle.
Requires further comment: What about Logging and Code Contract classes. They are ubiquitous. One work around is to use a static class for these, where the static class relays all calls onto an object interface.

July 29, 2021

Resolve URL without Page

Needed to resolve a URL from outside a controller/page in a worker class, I did not want to pass a page or controller to the class. Found the solution here: ResolveUrl

July 14, 2021

Producer Consumer using PLINQ

I took the idea from this web page. In this article a number of experiments were run to find the optimum way to run a Producer-Consumer using the TPL.

// This calculates distances between a target system and a bunch of other systems
// Results are returned as a dictionary of a system id to a double where the double
// represents the distance from the given system to the target system
public IDictionary<int, double> CalcSystemDistances(
    int targSystemId, 
    ISet<int> systemIds)
{
    var idToDistanceMap = new ConcurrentDictionary<int, double>();
    // _systemDb is a repository of systems
    var targSystem = _systemDb.Get(targSystemId); 
    if (targSystem != null)
    {
        int WorkerCount = Math.Max(Environment.ProcessorCount - 2, 4);

        // Setup the queue
        var blockingCollection = new BlockingCollection<SystemInfo>();

        // Declare the worker - Calculates a distance
        Action<SystemInfo> work = eddbSys =>
        {
            double dist = targSystem.Location.DistanceTo(eddbSys.Location);
            idToDistanceMap.TryAdd(eddbSys.Id, dist);
        };

        // Begin producing source data
        Task.Run(() =>
        {
            foreach (var eddbSys in _systemDb.Get(systemIds).Where(s => s != null))
            {
                blockingCollection.Add(eddbSys);
            }
            blockingCollection.CompleteAdding();
        });

        // Start consuming
        blockingCollection.
            GetConsumingEnumerable().
            AsParallel().
            WithDegreeOfParallelism(WorkerCount).
            WithMergeOptions(ParallelMergeOptions.NotBuffered).
            ForAll(work);

    }
    return idToDistanceMap;
}

July 12, 2021

Csv File Reader and Writer

Here are a simple Generic CSV File Writer and Reader. It is simple and lightly tested, it has not been tested in other locales. There is no exception handling. Parameters are checked using Debug.Assert() which will not check them in release mode, ... . Could refactor the StreamWriter/StreamRedaer as a parameter so that it could be used to read/write to a generic StreamWriter/Reader rather than a file specifically. Be careful with the separator, be sure that it will never occur in within the data. I have defaulted to a TAB character but the '|' character is pretty good too. Also be careful with DateTimes, I convert them to universal format, if you pass in a 'local' DateTime kind, it will be returned as a "Utc" kind. Generally, you should not be saving DateTimes as a local format, but rather as a Universal DateTime (although there are always exceptions).

First is the writer:
/// <summary>
/// Writes a set of data objects types to disk. 
/// The data objects must have only simple types as properties: 
///     string, bool, numeric types, enum types, DateTime
/// </summary>
/// <typeparam name="T">Data object type to save
/// Must expose the data as properties and 
/// have a default (ie. parameterless) constructor</typeparam>
public class CsvStreamWriter<T> where T : class, new()
{
    public int WriteCsvFile(
        FileInfo file, 
        IEnumerable<T> targets, 
        string separator = "\t")
    {
        int datalinesWritten = 0;
        using (StreamWriter writer = file.CreateText())
        {
            datalinesWritten = WriteCsvStream(writer, targets, separator);
        }
        return datalinesWritten;
    }

    public int WriteCsvStream(StreamWriter writer,
        IEnumerable<T> targets, 
        string separator = "\t")
    {
        Debug.Assert(writer != null);
        Debug.Assert(separator.Length > 0);
        Debug.Assert(targets != null);

        // Assuming here that there are not ridiculous numbers of properties
        // on the class, so using ToList() has no big overhead.
        // Properties must have get and set
        var properties = typeof(T).GetProperties().
            Where(p => p.CanRead && p.CanWrite).
            ToList();
        Debug.Assert(properties.Count > 0, $"Type {typeof(T)} has no Properties!");

        int count = 0;
        writer.WriteLine(GetCsvHeaderLine(properties, separator));
        foreach (var entry in targets.Where(x => x != null))
        {
            var propertiesLine = GetObjectAsCsvLine(entry, properties, separator);
            writer.WriteLine(propertiesLine);
        }
        return count;
    }

    private string GetCsvHeaderLine(List<PropertyInfo> properties, 
        string separator)
    {
        StringBuilder stringBuilder = new StringBuilder();
        for (var ix = 0; ix < properties.Count - 1; ix++)
        {
            stringBuilder.Append(properties[ix].Name).Append(separator);
        }
        stringBuilder.Append(properties[properties.Count - 1].Name);

        return stringBuilder.ToString();
    }

    private string GetObjectAsCsvLine(T instance, 
        List<PropertyInfo> properties, 
        string separator)
    {
        Debug.Assert(instance != null);
        StringBuilder stringBuilder = new StringBuilder();

        for (var ix = 0; ix < properties.Count; ix++)
        {
            var prop = properties[ix];
            var obj = prop.GetValue(instance, System.Reflection.BindingFlags.GetProperty, 
                null, null, System.Globalization.CultureInfo.InvariantCulture);
            if (prop.PropertyType.IsEnum)
            { // Write the enum to the file as an integer rather than a string
                var valueString = Enum.Format(prop.PropertyType, obj, "d");
                stringBuilder.Append(valueString);
            }
            else if (prop.PropertyType == typeof(DateTime))
            {
                // Generally, DateTime should be persisted in Universal time format, 
                // convert to local time format when writing to a GUI or a report
                var valueString = ((DateTime)obj).ToUniversalTime().ToString("o");
                stringBuilder.Append(valueString);
            }
            else
            {
                stringBuilder.Append(obj);
            }
            if (ix != properties.Count - 1) // Append the separator to all but the last.
            {
                stringBuilder.Append(separator);
            }
        }

        return stringBuilder.ToString();
    }
}
Next is the reader:
/// <summary>
/// Reads a set of data objects types written to a file.
/// Each line of the file has a data object in CSV format.
/// The very first line is a special header line, that describes
/// the order in which the data object properties are persisted 
/// on each line
/// The data objects must have only simple types as properties: 
///     string, bool, numeric types, enum types, DateTime
/// </summary>
/// <typeparam name="T">Data object type to save
/// Must expose the data as properties and 
/// have a default (ie. parameterless) constructor</typeparam>
public class CsvStreamReader<T> where T : class, new()
{
    public IEnumerable<T> ReadCsvFile(
        FileInfo file, 
        string separator = "\t")
    {
        Debug.Assert(file.Exists);

        return ReadCsvStream(file.ReadLines(), separator);
    }

    public IEnumerable<T> ReadCsvStream(
        IEnumerable<string> dataLines, 
        string separator = "\t")
    {
        Debug.Assert(separator.Length > 0);
        // Assuming here that there are not ridiculous numbers of properties
        // on the class, so using ToList() has no big overhead.
        // Properties must have get and set
        List<PropertyInfo> properties = typeof(T).GetProperties().
            Where(p => p.CanRead && p.CanWrite).
            ToList();

        var propertiesInReadOrder = new List<PropertyInfo>();

        int count = 0;
        foreach (var line in dataLines)
        {
            if (count == 0)
            { // First line must be header, listing the props in order in which they are read
                propertiesInReadOrder = GetCsvHeaders(line, separator, properties);
                Debug.Assert(propertiesInReadOrder.Count > 0,
"First line must be the header, listing the properties in the order in which they are read. This was not found.");
            }
            else
            {
                T instance = ParseCsvLine(line, separator, propertiesInReadOrder);
                if (instance != null)
                    yield return instance;
            }
            count++;
        }
    }

    private List<PropertyInfo> GetCsvHeaders(string line,
    string separator,
    List<PropertyInfo> properties)
    {
        // The CSV headers give the order in which the properties are read in
        var parts = line.Split(new[] { separator }, StringSplitOptions.None);

        var propertiesInOrder = new PropertyInfo[parts.Length];
        // Dictionary of property name to PropertyInfo (property name is case sensitive)
        Dictionary<string, PropertyInfo> nameToPropertyMap =
            properties.ToDictionary(p => p.Name, p => p);
        var ix = 0;
        PropertyInfo property = null;
        foreach (var entryRaw in parts)
        {
            var entry = entryRaw.Trim();
            propertiesInOrder[ix] = nameToPropertyMap.TryGetValue(entry, out property) ?
                                                property : null;
            ix++;
        }
        return propertiesInOrder.ToList();
    }

    private T ParseCsvLine(string line,
        string separator,
        List<PropertyInfo> propertiesInOrder)
    {
        string[] parts = line.Split(new[] { separator }, StringSplitOptions.None);
        T instance = new T();
        for (int ix = 0; ix < propertiesInOrder.Count; ix++)
        {
            if (propertiesInOrder[ix] != null)
            {
                if (propertiesInOrder[ix].PropertyType.IsEnum)
                {  // Enums are saved as numeric values rather than strings
                    // get the enum underlying numeric type
                    var underlyingType = Enum.GetUnderlyingType(propertiesInOrder[ix].PropertyType);
                    // Get the enum value as an underlying numeric type value
                    var decimalValue = Convert.ChangeType(parts[ix], underlyingType);
                    // Convert the enum numeric value to an enum
                    var enumValue = Enum.ToObject(propertiesInOrder[ix].PropertyType, decimalValue);
                    propertiesInOrder[ix].SetValue(instance, enumValue);
                }
                else if (propertiesInOrder[ix].PropertyType == typeof(DateTime))
                {
                    var value = Convert.ToDateTime(parts[ix]).ToUniversalTime();
                    propertiesInOrder[ix].SetValue(instance, value,
                        System.Reflection.BindingFlags.SetProperty, null, null,
                        System.Globalization.CultureInfo.InvariantCulture);
                }
                else
                {
                    var value = Convert.ChangeType(parts[ix], propertiesInOrder[ix].PropertyType);
                    propertiesInOrder[ix].SetValue(instance, value,
                        System.Reflection.BindingFlags.SetProperty, null, null,
                        System.Globalization.CultureInfo.InvariantCulture);
                }
            }
        }
        return instance;
    }
}
Some FileInfo extensions to make them easy to use:
public static class FileInfoCsvExtender
{
    public static int WriteCsvFile<T>(this FileInfo file, 
        IEnumerable<T> targets, 
        string separator = "\t") 
            where T : class, new()
    {
        var csvwriter = new CsvStreamWriter<T>();
        return csvwriter.WriteCsvFile(file, targets, separator);
    }
    public static IEnumerable<T> ReadCsvFile<T>(this FileInfo file, 
        string separator = "\t") 
            where T : class, new()
    {
        var reader = new CsvStreamReader<T>();
        return reader.ReadCsvFile(file, separator);
    }
}
then to write a CSV file:
  List<TestStation> testStations = ... ;
  FileInfo fi = new FileInfo(@"~\some\path\TestStations.tsv");
  fi.WriteCsvFile<TestStation>(testStations);
and then to read it:
  FileInfo fi = new FileInfo(@"~\some\path\TestStations.tsv");
  List<TestStation> stations = fi.ReadCsvFile<TestStation>().ToList();
Here is a unit test to verify that it works:
    private IEnumerable<TestStation> TestData()
    {
        yield return new TestStation 
        { 
            Id = 2, HasMarket = false, Coordinate = 123.345f, Name = "Gehry Dock", 
            LandingPadSizes = LandingPadEnums.Small | LandingPadEnums.Medium, 
            DateTime = new DateTime(2021, 3, 28, 15, 10, 56, DateTimeKind.Utc) 
        };
        yield return new TestStation 
        { 
            Id = 3, HasMarket = true, Coordinate = -435.1f, Name = "Trevithick Terminal", 
            LandingPadSizes = LandingPadEnums.Small | LandingPadEnums.Medium | LandingPadEnums.Large, 
            DateTime = new DateTime(2021, 4, 12, 3, 1, 6, DateTimeKind.Utc) 
        };
        yield return new TestStation 
        { 
            Id = 1, HasMarket = true, Coordinate = 915.0f, Name = "Bain End", 
            LandingPadSizes = LandingPadEnums.Small, 
            DateTime = new DateTime(2019, 9, 7, 0, 45, 0, DateTimeKind.Utc) 
        };
    }

    [Test]
    public void SaveAndLoadTest()
    {
        List<TestStation> original = TestData().ToList();
        WriteStationsToCsvFile(original);
        ReadStationsFromCsvFile(original);
    }

    private void ReadStationsFromCsvFile(List<TestStation> original)
    {
        FileInfo fi = new FileInfo(Environment.ExpandEnvironmentVariables(@"%Temp%") + @"\TestStations.tsv");
        List<TestStation> stations = fi.ReadCsvFile<TestStation>().ToList();

        Trace.WriteLine($"*** {stations.Count} stations read from file \"{fi.FullName}\" ");

        Assert.AreEqual(stations.Count, original.Count);
        Assert.AreEqual(LandingPadEnums.Small | LandingPadEnums.Medium, stations[0].LandingPadSizes);
        Assert.AreEqual("Gehry Dock", stations[0].Name);
        Assert.IsTrue(stations[0].Coordinate == 123.345f);
        Assert.IsTrue(stations[0].Id == 2);
        Assert.IsFalse(stations[0].HasMarket);
        Assert.IsTrue(stations[0].DateTime == new DateTime(2021, 3, 28, 15, 10, 56, DateTimeKind.Utc));

        Assert.AreEqual("Trevithick Terminal", stations[1].Name);
        Assert.IsTrue(stations[1].Id == original[1].Id);
        Assert.IsTrue(stations[1].Coordinate == -435.1f);

        Assert.AreEqual("Bain End", stations[2].Name);
        Assert.IsTrue(stations[2].Id == 1);
        Assert.IsTrue(stations[2].HasMarket);
        Assert.IsTrue(stations[2].DateTime == new DateTime(2019, 9, 7, 0, 45, 0, DateTimeKind.Utc));
        Assert.AreEqual(LandingPadEnums.Small, stations[2].LandingPadSizes);

    }

    private void WriteStationsToCsvFile(List<TestStation> original)
    {
        Trace.WriteLine($"*** {original.Count} Stations found on server \"{TestServerNames.LocalSqlServerName}\": ");

        FileInfo fi = new FileInfo(Environment.ExpandEnvironmentVariables(@"%Temp%") + @"\TestStations.tsv");
        if (fi.Exists)
        {
            fi.Delete();
            fi.Refresh();
        }

        Assert.That(!fi.Exists);
        fi.WriteCsvFile(TestData());
        fi.Refresh();
        Assert.That(fi.Exists);

        Trace.WriteLine($"*** {original.Count} stations written to file \"{fi.FullName}\" ");
    }
and the CSV file looks like this:
Id	Coordinate	HasMarket	LandingPadSizes	Name	DateTime
2	123.345	False	3	Gehry Dock	2021-03-28T15:10:56.0000000Z
3	-435.1	True	7	Trevithick Terminal	2021-04-12T03:01:06.0000000Z
1	915	True	1	Bain End	2019-09-07T00:45:00.0000000Z

July 4, 2021

Extensions For FileInfo class

Extend FileInfo with methods to read all the lines of a file or write a series of lines to a file. The beauty of these methods is that they are simple and use Linq lazy evalution so that the files can be read/written a line at a time.

public static class FileInfoExtender
{
  public static IEnumerable<string> ReadLines(this FileInfo fi)
  {
      return File.ReadLines(fi.FullName);
  }

  public static IEnumerable<string> ReadLines(this FileInfo fi, Encoding encoding)
  {
      return File.ReadLines(fi.FullName, encoding);
  }

  public static string ReadAllText(this FileInfo fi)
  {
      var res = File.ReadAllText(fi.FullName);
      return res;
  }

  public static async Task<string> ReadAllTextAsync(this FileInfo fi)
  {
      var res = await File.ReadAllTextAsync(fi.FullName);
      return res;
  }

  public static void WriteLines(this FileInfo fi, IEnumerable<string> lines)
  {
      File.WriteAllLines(fi.FullName, lines);
  }

  public static void WriteLines(this FileInfo fi, IEnumerable<string> lines, Encoding encoding)
  {
      File.WriteAllLines(fi.FullName, lines, encoding);
  }

  public static string FileNameWithoutExtension(this FileInfo fi)
  {
      return Path.GetFileNameWithoutExtension(fi.Name);
  }
}

June 22, 2021

Implementing a Batch in Linq

When enumeration database records in Linq, it is important to batch the work otherwise LINQ can generate SQL statements that are too big (regardless of whether Entity Frameworks are being used or LINQ to SQL). Another problem is that it can take a long time to process all the records, not only can that require a lot of memory, after a while you will start to wonder if the code has crashed or not. It is better to see a batch finishing every now and then and thus know that it is actually progressing.
// Source here : https://stackoverflow.com/questions/13731796/create-batches-in-linq
// Example here : https://dotnetfiddle.net/HpRgd5
public static class BatchLinq
{
    /// <summary>
    /// Full lazy implementation of a batch
    /// Known issue with this approach is that each batch must be 
    /// enumerated and enumerated fully before moving to the next batch.
    /// </summary>
    /// <typeparam name="T">The type being enumerated</typeparam>
    /// <param name="source">The source enumerable</param>
    /// <param name="size">Size of the batch</param>
    /// <returns>An enumeration of batches with size elements 
    /// in each batch</returns>
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
        {
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
        }
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                var innerBatch = new InnerBatch();
                var enumerable = innerBatch.Batch(enumerator, size);
                yield return enumerable;
                if (!innerBatch.IsFinished)
                    enumerable.Count();
            }
        }
    }

    private class InnerBatch
    {
        public bool IsFinished { get; private set; } = false;

        public IEnumerable<T> Batch<T>(IEnumerator<T> source, int size)
        {
            int i = 0;
            do 
                yield return source.Current;
            while (++i < size && source.MoveNext());
            IsFinished = true;
        }
    }

    // This implementation splits the enumeration up into batches of lists
    // This is safe to use if the batches are small but it is not a fully lazy 
    // evaluation of the batch like the above method. However if you do not 
    // enumerate each batch fully then this version must be used
    public static IEnumerable<IList<T>> BatchByList<T>(this IEnumerable<T> source, int size)
    {
        List<T> batch = new List<T>(size);

        foreach (var item in source)
        {
            batch.Add(item);

            if (batch.Count >= size)
            {
                yield return batch;
                batch = new List<T>(size);
            }
        }

        if (batch.Count > 0)
        {
            yield return batch;
        }
    }
}

Using AutoFac for Dependency Injection

Create the Autofac builder:
    // Step 1. Create the builder with which components/services are registered.
    var builder = new ContainerBuilder();

    // Step 2. 
    RegisterTypesWithAutoFac(builder); // Register your types here (see below)

    // Step 3.
    // Build the container to finalize registrations
    // and prepare for object resolution.
    var container = builder.Build();

    // Step 4.
    // Now you can resolve services using Autofac.
    using(var scope = container.BeginLifetimeScope())
    {
      var reader = scope.Resolve<IConfigReader>();
    }
However in MVC 5 replace Step 4 with this:
    var mvcResolver = new AutofacDependencyResolver(container);
    DependencyResolver.SetResolver(mvcResolver);

Registering Types
Here are a few key examples of registering types.
Case 1: Instance on Request:

builder.RegisterType<SystemDistanceCalculator>().
    As<ISystemDistanceCalculator>().
    InstancePerRequest();
Case 2: A singleton instance
builder.RegisterType<SystemIdsCachesManager>().
    As<ISystemIdsCachesManager>().
    SingleInstance();
Case 3: Initializing Parameters Example
builder.RegisterType<PermitSystemsFactory>().
    As<IPermitSystemsFactory>().
    WithParameter("repositoryPath", EliteDangerousPaths.PermitSystemsJsonlPath).
    SingleInstance();
Case 4: Using a lambda to perform some of the object initialization
builder.Register(c =>
{
    var edSystemsDb = new EdSystemsSqlDb();
    var sqlServerConfig = c.Resolve<ISqlServerConfiguration>();
    edSystemsDb.Initialise(sqlServerConfig.ConnectionString);
    return edSystemsDb;
}).
    As<IEdSystemsDb>().
    SingleInstance();

May 5, 2021

c# GetUniqueFlags for Flags Enum

Use this to get unique flags from a combined flags enum value
public static class EnumFlagExtensions
{
    // https://stackoverflow.com/questions/4171140/how-to-iterate-over-values-of-an-enum-having-flags
    public static IEnumerable<T> GetUniqueFlags<T>(this T value)
        where T : Enum
    {
        // if it's not a flag enum, return empty
        Debug.Assert(value.GetType().IsDefined(typeof(FlagsAttribute), false), 
          "Not a \"Flags\" enum type");
        if (!value.GetType().IsDefined(typeof(FlagsAttribute), false))
          yield break;

        var valueLong = Convert.ToUInt64(value, CultureInfo.InvariantCulture);
        foreach (var enumValue in value.GetType().GetEnumValues())
        {
            if (
                enumValue is T flag // cast enumValue to T
                // convert flag to ulong
                && Convert.ToUInt64(flag, CultureInfo.InvariantCulture) is ulong bitValue 
                && (bitValue & (bitValue - 1)) == 0 // is this a single-bit value?
                && (valueLong & bitValue) != 0 // is the bit set?
                )
            {
                yield return flag;
            }
        }
    }
}

April 1, 2021

Double String Format Extensions

For the Microsoft documentation see see here
Makes writing numeric types as a formatted number much easier. Here it is set for double types:

void Main()
{
	Console.WriteLine(1234.567.ToFixedDpFormat(2));      // =>  1234.57
	Console.WriteLine(1234.567.ToNumSigDigitsFormat(3)); // =>  1.23E+03
	Console.WriteLine(1234.567.ToCulturalFormat(2));     // =>  1,234.57
	Console.WriteLine(1234.567.ToCurrencyFormat());      // => £1,234.57

	Console.WriteLine(1234d.ToFixedDpFormat(2));            // =>  1234.00
	Console.WriteLine(1.2340.ToNumSigDigitsFormat(3));      // =>     1.23
	Console.WriteLine(123445677.345234.ToCulturalFormat(2));// => 123,445,677.35
	Console.WriteLine(1234345.567.ToCurrencyFormat());      // => £1,234,345.57
}

namespace Common.Extensions.DoubleToStringFormatting
{
  public static class DoubleToStringExtensions
  {
    public static string ToFixedDpFormat(this double number, int numDP = 0)
    {
      var res = number.ToString($"F{numDP}"); // ToFixedDpFormat(1234.567,2) (en - US) -> 1234.57
      return res;
    }


    public static string ToNumSigDigitsFormat(this double number, int numSigDig = 3)
    {
      var res = number.ToString($"G{numSigDig}"); // ToNumSigDigitsFormat(123.4546) en-US -> 124.0
      return res;
    }

    // Prettifies the string according to cultural aesthetics and the specified num of DP
    public static string ToCulturalFormat(this double number, int numDP = 2)
    {
      var res = number.ToString($"N{numDP}");
      return res;
    }

    // Converts to a currency (using the default cultural currency sign)
    public static string ToCurrencyFormat(this double number, int numDP = 2)
    {
      var res = number.ToString($"C{numDP}"); // ToCurrencyFormat(123.456), en-US -> $123.46
      return res;
    }
  }
}

Something similar can be used for float,decimal and int types as well.

March 11, 2021

C# Windows File Explorer Openers

This class will Open Windows File Explorer and get it to open a directory and optionally select a specified file in the directory

/// <summary>
/// Windows Explorer Helperes
/// </summary>
public class FileExplorerOpener
{
/// <summary>
/// Open Windows File Explorer, in it open a directory and select
/// a specific file
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public bool OpenDirectoryAndSelectFile(string filePath)
{
var fi = new FileInfo(filePath);
var outcome = fi.Exists;
if (outcome)
{
var argument = "/select, \"" + fi.FullName + "\"";
Process.Start("explorer.exe", argument);
}
return outcome;
}

/// <summary>
/// Open Windows Explorer, in it open the directory or if
/// a file path is passed in, the directory in which the
/// file is located
/// </summary>
/// <param name="inputPath">Directory or file path</param>
public void OpenFileDirectory(string inputPath)
{
string path = System.IO.Path.GetDirectoryName(inputPath);
if (System.IO.Directory.Exists(path))
{
Process.Start(path);
}
}
}

March 4, 2021

Search File Contents with LinqPad

Searching the contents of some file or files in LinqPad (a line at a time) in C# or even just finding a file:
Directory.EnumerateFiles(
  @"C:\Path\To\Search\Directory\", // Target of search
  "*.txt", // Specify file or files to search
  SearchOption.AllDirectories).  
  // SearchOption.AllDirectories - recusively search subdirectories
  // SearchOption.TopDirectoryOnly - search specified directory only
  // Next line records a line of text, the line number, and the file
  SelectMany(file => File.ReadAllLines(file).Select((text,n) => new {text,lineNumber=n+1,file})).
  // Here we search the text
  // Use Regex for the search OR use string methods Contains(), StartsWith, EndsWith ...
  Where(line => Regex.IsMatch(line.text, @"Searching for this text", RegexOptions.IgnoreCase)
                                            && line.text.Contains("ERROR") ).
  Dump("Matches found")
When debugging you can always use Skip() and Take() to reduce the number of lines searched.
A similar one but this uses a LinqPad HyperLinq object to use Notepad++ to open the file at the given line number.
// https://npp-user-manual.org/docs/command-prompt/
var notepadppPath = @"C:\Program Files (x86)\Tools\Notepad++\notepad++.exe";
Directory.EnumerateFiles(
   @"C:\Repos\",  // Directory
   "*.csproj", SearchOption.AllDirectories) // File names
   .SelectMany(file => File.ReadAllLines(file).Select((text, n) =>
		new
		{   
			file = new Hyperlinq(() => Process.Start(notepadppPath, 
				file+" -n"+(n+1).ToString()), file),
			lineNumber = n + 1,
			text
		}))
   .Where(x => // WHat you are looking for in the file
  // Regex.IsMatch(line.text, @"^.*tlpNotifySystemShutdown.*$"))
     x.text.Contains("DownloadProcess.Contracts") // 1.0.1
  || x.text.Contains("Foobar.Client")  // 13.15.0
  || x.text.Contains("DownloadProcess.Client") // 1.2.0
  || x.text.Contains("XxxZipCreator.")  // 5.1.0
  || x.text.Contains("Sloopy.Crial.Messages") // 1.6.3
   )
   .Dump("Matches found");
Here is another example:
Directory.EnumerateFiles(
  @"C:\Path\To\Search\Directory\",
  "*.cs", SearchOption.AllDirectories)
  .SelectMany(file => File.ReadAllLines(file).Select((text, n) => new { text, lineNumber = n + 1, file }))
  .Where(line => line.text.Contains("[CallerMemberName"))
  .Dump("Matches found")

February 24, 2021

DateTime String Format Extensions

Bear in mind that a DateTime should generally be stored in Universal format and converted to local format when be displayed in a GUI or used in a report. This handy extension for date time format strings saves having to remember their obscure format codes or look them up every time.

// See here for more info 
// https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
public static class DateTimeToStringExtender
{
    public static bool ParseGeneralShortDateTime(this string dateTimeStr, out DateTime dateTime)
    {
        var format = "g"; // Standard short date time format: "dd/MM/yyyy HH:mm:ss" for UK
        bool result = false;
        CultureInfo provider = CultureInfo.CurrentCulture;
        result = DateTime.TryParseExact(dateTimeStr.Trim(), format, provider, DateTimeStyles.None, out dateTime);
        return result;
    }

    /// <summary>
    /// Convert a DateTime to a YearMonthDay string of the form yyyy/MM/dd
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>YearMonthDay string of the form yyyyMMdd</returns>
    public static string ToYyyyMmDdString(this DateTime dt, char separator = char.MinValue)
    {
        string format = "yyyyMMdd";
        if (!char.IsControl(separator))
        {
            format = "yyyy" + separator + "MM" + separator + "dd";
        }
        var res = dt.ToString(format);
        return res;
    }

    /// <summary>
    /// Convert a DateTime to a YearMonthDay string of the form DdMmYyyy
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>YearMonthDay string of the form DdMmYyyy</returns>
    public static string ToDdMmYyyyString(this DateTime dt, char separator = char.MinValue)
    {
        string format = "ddMMyyyy";
        if (!char.IsControl(separator))
        {
            format = "dd" + separator + "MM" + separator + "yyyy";
        }
        return dt.ToString(format);
    }


    /// <summary>
    /// Convert a DateTime to a YearMonthDay string of the form yyyyMMdd_HHmmss where the hour is in the 24 hour format
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>YearMonthDay string of the form yyyyMMdd_HHmmss</returns>
    public static string ToYyyyMmDd_HhMmSsString(this DateTime dt)
    {
        return dt.ToString("yyyyMMdd_HHmmss");
    }

    /// <summary>
    /// Convert a DateTime to a YearMonthDay string of the form yyyyMMdd_HHmmss_ffffff where the hour is in the 24 hour format
    /// Useful for creating randomised filenames based upon the date
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>YearMonthDay string of the form yyyyMMdd_HHmmss_ffffff</returns>
    public static string To_YyyyMmDd_HhMmSs_ffffff(this DateTime dt)
    {
        return dt.ToString("yyyyMMdd_HHmmss_ffffff");
    }

    /// <summary>
    /// Convert a DateTime to a YearMonthDay string of the form yyyyMMdd_HHmmss where the hour is in the 24 hour format
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>YearMonthDay string of the form yyyyMMdd_HHmmss</returns>
    public static string ToYyyyMmDd_HhMmSsString(this DateTime dt, char dateSeparator = char.MinValue, char timeSeparator = char.MinValue)
    {
        string format = "yyyyMMdd_HHmmss";
        if (!char.IsLetterOrDigit(dateSeparator) && !char.IsControl(dateSeparator))
        {
            format = "yyyy" + dateSeparator + "MM" + dateSeparator + "dd" + "_" + "HH" + timeSeparator + "mm" + timeSeparator + "ss";
        }
        return dt.ToString(format);
    }


    /// <summary>
    /// Emits a date time (culture independent) string of the form 
    /// "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"
    /// (eg.: "Sun, 09 Mar 2008 16:05:07 GMT"). 
    /// Works on universal date times only
    /// </summary>
    /// <param name="dateTime">target date time to convert to a string</param>
    /// <returns>dateTime formatted as a string</returns>
    public static string ToRfc1123FormatString(this DateTime dateTime)
    {
        string result = dateTime.ToUniversalTime().ToString("r");
        return result;
    }

    /// <summary>
    /// Convert a DateTime to a string of the form "2008-03-09 16:05:07Z"
    /// where the '-' character is culture dependant
    /// </summary>
    /// <param name="dt">target</param>
    /// <returns>string of the form "2008-03-09 16:05:07Z" where '-' character is culture dependant</returns>
    public static string ToUniversalSorta­bleString(this DateTime dt)
    {
        return dt.ToUniversalTime().ToString("u");
    }

    // 2009-06-15T13:45:30 --> Monday, June 15, 2009 8:45:30 PM (for en-US)
    public static string ToUniversalFull(this DateTime dateTime)
    {
        var res = dateTime.ToUniversalTime().ToString("U");
        return res;
    }

    /// <summary>
    /// Emits a sortable date time (culture independent) string of the form "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (eg.: "2008-03-09T16:05:07"")
    /// </summary>
    /// <param name="dateTime">target date time to convert to a string</param>
    /// <returns>dateTime formatted as a string</returns>
    public static string ToSortableFormatString(this DateTime dateTime)
    {
        string result = dateTime.ToString("s");
        return result;
    }

    /// <summary>
    /// Emits a result date time format string using a pattern that preserves
    /// time zone information and emits a result string that complies with
    /// ISO 8601 ("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"), preserves 
    /// the DateTime kind.
    /// </summary>
    /// <param name="dateTime">target date time to convert to a string</param>
    /// <returns>dateTime formatted as a string</returns>
    // 2009-06-15T13:45:30 (DateTimeKind.Utc) -->; 2009-06-15T13:45:30.0000000Z
    // 2009-06-15T13:45:30 (DateTimeKind.Local) --> 2009-06-15T13:45:30.0000000-07:00 (depending on the time zone)
    // 2009-06-15T13:45:30 (DateTimeKind.Unspecified) --> 2009-06-15T13:45:30.0000000
    public static string ToRoundTripFormatString(this DateTime dateTime)
    {
        string result = dateTime.ToString("o");
        return result;
    }

    static readonly string[] formats = { 
    // Extended formats
    "o",
    "yyyy-MM-ddTHH:mm:ss.ffffffZ",
    "yyyy-MM-ddTHH:mm:ss.fffffZ",
    "yyyy-MM-ddTHH:mm:ss.ffffZ",
    "yyyy-MM-ddTHH:mm:ss.fffZ",
    "yyyy-MM-ddTHH:mm:ss.ffZ",
    "yyyy-MM-ddTHH:mm:ss.fZ",
    "yyyy-MM-ddTHH:mm:ssZ"
  };

    public static DateTime ParseToIso8601DateTime(this string str)
    {
        return DateTime.ParseExact(str, formats,
            CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
    }

}

February 16, 2021

Compare Files Using Visual Studio

Go to the command window:
Tools.DiffFiles {file1path} {file2path}

You can get the file paths using Windows Explorer, select the file with the right mouse button whilst holding down shift and then select the menu option "Copy as path"

Extract MSI files (without installing)

Extract MSI files (without installing) With “Admin” permission use the following command line:
msiexec /a drive:\filepath\to\MSI\file /qb TARGETDIR=drive:\filepath\to\target\folder        
For example to extract “Product.Setup.msi” files:
msiexec /a Product.Setup.msi /qb TARGETDIR=C:\Downloads\Product.Setup\Extracted

Using Test Files in a Unit Test with NUnit

Insert the files into the project under a new directory say “TestData”. Include them in the project and change the build action on each file such that the “Copy to Output Directory” option should be “Copy if newer”.

Somewhere in your test:
// Create a path to the TestData directory using the 
// NUnit “TestContext.CurrentContext.TestDirectory” property
private string TestFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData");
// Now you have access to the files
string testFilePath = Path.Combine(TestFilePath, @"TestFile1.mib");

Example of using Moq's MockSequence in a Unit Test

This test shows how to test Properties and Methods
[Test]
[Category("AutomaticTest")]
public void Some_Test()
{
    var mockPublisher = new Mock(MockBehavior.Strict);
    var sequence = new MockSequence();
    mockPublisher.InSequence(sequence).Setup(x => x.Start());
    mockPublisher.InSequence(sequence).SetupSet(x => x.IsSynchronizing = true);
    mockPublisher.InSequence(sequence).Setup(x => x.Start());
    mockPublisher.InSequence(sequence).Setup(x => x.Complete());
    mockPublisher.InSequence(sequence).Setup(x => x.Start());
    mockPublisher.InSequence(sequence).Setup(x => x.Complete());
    mockPublisher.InSequence(sequence).Setup(x => x.Complete());
    mockPublisher.InSequence(sequence).SetupSet(x => x.IsSynchronizing = false);

    var fakeSyncObserver = new FakeSystemReportersSyncObserver();
    var fakeConnectivityObserver = new FakeEventEngineConnectivityObserver();
    Monitor Monitor = new Monitor(mockPublisher.Object, fakeSyncObserver, fakeConnectivityObserver);

    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("1", 
      SyncEnum.ReportSynchronized));    // Start(), IsSynchronizing = true
    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("2", 
      SyncEnum.ReportSynchronized));    // Start() (IsSynchronizing is already true)
    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("2", 
      SyncEnum.Completed)); // Complete() ("1" is still syncing)
    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("2", 
      SyncEnum.ReportSynchronized));    // Start() (IsSynchronizing is already true)
    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("2", 
      SyncEnum.Completed)); // Complete() ("1" is still syncing)
    fakeSyncObserver.PublishStateChanged(new SyncEventArgs("1", 
      SyncEnum.Completed)); // Complete(), IsSynchronizing = false ("1" and "2" are both complete now)

    mockPublisher.VerifyAll();
    mockPublisher.Verify(m => m.Fail(), Times.Never())
    mockPublisher.Verify(m => m.Start(), Times.Exactly(3));
}
MockSequence has a bug, make sure that there is a call of "VerifyAll()" followed by at least one call to "Verify()" otherwise the MockSequence may not actually be checked!
Here is another sample test:
[Test]
[Category("AutomaticTest")]
public void AnotherSampleTest()
{
    var mockSnmpPublisher = new Mock(MockBehavior.Strict);
    // Create the MockSequence to validate the call order
    var sequence = new MockSequence();
    // Create the expectations, notice that the Setup is called via InSequence
    mockSnmpPublisher.InSequence(sequence).Setup(
        x => x.SendTrap(
            It.Is(y => y.Equals(StatusOids.Traps.SynchronizationStarted)),
            It.Is(s => s == true)));
    mockSnmpPublisher.InSequence(sequence).
      Setup(x => x.SetVariable(It.Is(y => y.Equals(StatusOids.Variables.IsSynchronizing)), 
                      It.Is(y => y.Equals(IsSynchronizing))));
    mockSnmpPublisher.InSequence(sequence).Setup(
        x => x.SendTrap(
            It.Is(y => y.Equals(StatusOids.Traps.SynchronizationCompleted)),
            It.Is(s => s == true)));
    mockSnmpPublisher.InSequence(sequence).
           Setup(x => x.SetVariable(It.Is(y => y.Equals(StatusOids.Variables.IsSynchronizing)), 
                    It.Is(y => y.Equals(IsNotSynchronizing))));

    SyncSnmpPublisher SyncSnmpPublisher = new SyncSnmpPublisher(mockSnmpPublisher.Object);
    SyncSnmpPublisher.Start();
    SyncSnmpPublisher.IsSynchronizing = true;
    SyncSnmpPublisher.Complete();
    SyncSnmpPublisher.IsSynchronizing = false;

    mockSnmpPublisher.VerifyAll();
    mockSnmpPublisher.Verify(m =>  // REMEMBER this will ensure the MockSequence will be checked
        m.SendTrap(It.Is(y => y.Equals(StatusOids.Traps.SynchronizationCompleted)), 
            It.Is(s => s == true)), Times.Once());
}

February 15, 2021

Hard Symbolic Links For Developers

Had a problem where the output of a (DEBUG) build went to one directory but the to run it needed to go somewhere else. This was easy to circumvent using hard links. Here is the command line for making a hard link (requires Administrator permissions)
mklink /J {SourceDirectory} {TargetDirectory}

The Source directory must NOT exist and the Target directory must. This is because the Source directory is created as a symbolic link to the hard directory
Here was my script:
mklink /J "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Plugins\ICMP" 
          "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Service\Plugins\ICMP"
mklink /J "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Plugins\RelayBoard" 
          "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Service\Plugins\RelayBoard"
mklink /J "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Plugins\SeveritySetter" 
          "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Service\Plugins\SeveritySetter"
mklink /J "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Plugins\SMTP" 
          "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Service\Plugins\SMTP"
mklink /J "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Plugins\WMI" 
          "C:\Src\repos\XxxRepo\bin\Debug\TheProduct.Service\Plugins\WMI"