November 18, 2020

Conditional Null Operator ?.

expression1?.expression2  => expression1 != null ? expression2 : null
This expression evaluates to: expression2, if expression1 is Not-Null. else, it evaluates to null
What happens when a conditional operator is inside a boolean expression?
void Main()
{
    A a = new A();
    if (a?.BVal?.Value == 4)
	{
	    Console.WriteLine("boolean expression returns true");
	}
	else
	{
		Console.WriteLine("boolean expression returns false"); // This line is output
	}
}

public class B
{
   public int Value { get; set; }
}

public class A
{
   public B BVal { get; set; }
}
A related operator is ??=
variable ??= expression // if (variable is null) { variable = expression; }

September 29, 2020

Extension methods to Convert Strings to Various Fundamental types

These extension methods converts strings to various types (int, bool, float)
When the string is invalid they return a fallback value.
public static class StringParserExtensions
{
	public static int ParseInt(this string part, int fallbackValue = 0)
	{
		if (!int.TryParse(part.Trim(), out int anInt))
		{
			anInt = fallbackValue;
		}
		return anInt;
	}

	public static bool ParseBool(this string part, bool fallbackValue = false)
	{
		if (!bool.TryParse(part.Trim(), out bool aBool))
		{
			aBool = fallbackValue;
		}
		return aBool;
	}

	public static float ParseFloat(this string part, float fallbackValue = 0.0f)
	{
		if (!float.TryParse(part.Trim(), out float number))
		{
			number = fallbackValue;
		}
		return number;
	}
}
Example usage:
  stationLocation.StationId = parts[0].ParseInt(0); 

Linq GroupBy/ToLookUp

These are Linq methods to group elements in a sequence. ToLookup() is the same as GroupBy(); the only differences are that GroupBy execution is deferred, whereas ToLookup() execution is immediate and also ToLookup() is not available in linq query form only the method form.

Note that the resultant group object exposes itself in 2 ways:

1. A 'Key' property exposes a value which all the items in the group have in common (this could be a calculated value)

2. The group object itself is an IEnumerable of the items in the group.

This operation will iterate over all the items in the enumeration. Works well with the Linq method "ToDictionary()" to convert a sequence into a dictionary.

Here is an example:

// Read StationLocationRaw(s) (int StationId and string Location)
// from the Station Locations file
public class StationLocationReader 
{
    FileInfo fi;
    public StationLocationReader(string path)
    {
        fi = new FileInfo(path);
    }

    public IDictionary<int, string> StationLocationsByStationId()
    {
        var stationLocationsDictionary =
            ReadStationLocations(). // returns StationLocationRaw(s)
                                    // (int StationId and string Location)
            GroupBy(s => s.StationId).
            ToDictionary(g => g.Key, g => g.Single().Location ?? "");
        return stationLocationsDictionary;
    }

    public IEnumerable<StationLocationRaw> ReadStationLocations()
    {
        Debug.Assert(fi.Exists);
        var stationLocations = fi.
            ReadLines().
            Skip(1). // Skip the header line
            Select(line => ExtractStationLocation(line));
        return stationLocations;
    }

    private StationLocationRaw ExtractStationLocation(string line)
    {
        var stationLocation = new StationLocationRaw();
        var parts = line.Split('\t');
        if (parts.Length >= 2)
        {
            // ParseInt is an extension method to convert
            // to a string to an integer, returning 0 
            // when the string is invalid.
            stationLocation.StationId = parts[0].Trim().ParseInt(0); 
            stationLocation.Location = parts[1].Trim();
        }
        return stationLocation;
    }
}

February 9, 2020

Sample Regular Expression

[TestMethod]
public void ParseVideoFileForDateTime2Test()
{
    var filePath = @"D:\Temp\Nokia 6\Photos\OpenCamera\VID_20190623_085427.mp4";
    var fileNameSansExt = Path.GetFileNameWithoutExtension(filePath);

    var dateTimeFormat = "yyyyMMdd_HHmmss";
    var result = string.Empty;
    Regex regExp = null;
    CalcRegExpForDateTimeFormat(dateTimeFormat, out regExp);
    if (regExp != null)
    {
        result = regExp.MatchFirstRegExp(fileNameSansExt);
    }
    Assert.IsNotNull(regExp);
    Assert.IsTrue(result.Length == 15);
    Assert.IsTrue(result == "20190623_085427");
}

private static bool CalcRegExpForDateTimeFormat(
    string dateTimeFormat, out Regex regExp)
{
    var res = false;
    regExp = null;
    var newRegex = new StringBuilder(dateTimeFormat.Length);
    foreach (var ch in dateTimeFormat)
    {
        if ((ch == 'y') || (ch == 'M') || (ch == 'd') ||
            (ch == 'H') || (ch == 'm') || (ch == 's'))
        {
            newRegex.Append(@"\d");
        }
        else
        {
            newRegex.Append(ch);
        }
    }
    var regexStr = newRegex.ToString();
    res = (regexStr.Length > 0);
    if (res)
    {
        regExp = new Regex(regexStr, RegexOptions.Compiled);
    }
    return res;
}

public static class RegularExpressionExtensions
{
/// <summary>
/// Match the first occurance of a regular expression in a target string.
/// </summary>
/// <param name="target">string to search</param>
/// <param name="regexp">regular expression object to use for the
/// search</param>
/// <returns>first match of the regular expression otherwise an 
/// empty string if there is no match</returns>
  public static string MatchFirstRegExp(this Regex reg, string target)
  {
    var result = "";
    MatchCollection mc = reg.Matches(target);
    if (mc.Count > 0)
    {
        result = mc[0].Value;
    }
    return result;
  }
}

November 14, 2019

Sql Server Version Finder using Value Tuples in C# Version 8

Here is an example of using the new tuples in Version 8 of the C# compiler. I've tried to use these tuples in a way which makes them as readable as possible.

public class SqlVersionFinder
{
  public (string version, string date, string copyright, string edition)
                          GetSqlVersion(string server = @".\")
  {
    // Use named arguments so that the tuple is easy to read and maintain
    var res = (version: "", date: "", copyright: "", edition: "");

    using (var connection = new SqlConnection(String.Format(
        "Server={0};Database=master;Trusted_Connection=True;",
         server)))
    {
      connection.Open();
      var sqlResult = "";

      using (var command = connection.CreateCommand())
      {
        command.CommandText = " SELECT @@VERSION";
        sqlResult = command.ExecuteScalar() as string ?? "";
      }
      if (!string.IsNullOrEmpty(sqlResult))
      {
        var versionInfo = sqlResult.Split(new[] { "\n\t" }, 
                            System.StringSplitOptions.None);
        if (versionInfo != null && versionInfo.Length >= 4)
        {
           res.version = versionInfo[0] ?? "";
           res.date = versionInfo[1] ?? "";
           res.copyright = versionInfo[2] ?? "";
           res.edition = versionInfo[3] ?? "";
        }
      }
    }
    return res;
  }
}
Here is the test code (from a Console app) that retrieves Sql Server Version information
SqlVersionFinder sqlVersionFinder = new SqlVersionFinder();
var res = sqlVersionFinder.GetSqlVersion();
Console.WriteLine("Version:   " + res.version);
Console.WriteLine("Date:      " + res.date);
Console.WriteLine("Copyright: " + res.copyright);
Console.WriteLine("Edition:   " + res.edition);
Output looks like this:

Version: Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (X64)
Date: Apr 29 2016 23:23:58
Copyright: Copyright (c) Microsoft Corporation
Edition: Standard Edition (64-bit) on Windows 10 Enterprise 6.3 (Build 17134: )


Console Application Template

Here is a simple template for console applications, including how to change the font color within the console

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("Started SomeConsoleApplication");
    Console.WriteLine("");

    Console.ForegroundColor = ConsoleColor.Green; // Go/all is good colour
    try
    {
      // Do your console app work here
      SqlVersionFinder sqlVersionFinder = new SqlVersionFinder();
      var res = sqlVersionFinder.GetSqlVersion();
      Console.WriteLine("Version:   " + res.version);
      Console.WriteLine("Date:      " + res.date);
      Console.WriteLine("Copyright: " + res.copyright);
      Console.WriteLine("Edition:   " + res.edition);

    }
    catch (Exception ex)
    {
      Console.WriteLine("Oops, an exception occurred trying to run the application: "
                                  + ex.ToString());
      Console.ForegroundColor = ConsoleColor.Red; // Error colour
      Console.ResetColor();
    }

    Console.ResetColor();
    // This will stop the console application from finishing until a key is pressed
    // However you might not want this if the application is to be run from another
    // process or you might want to use a command line argument to enable or 
    // disable this option
    Console.WriteLine("");
    Console.WriteLine("Press any key to finish ...");
    Console.ReadKey(); 
  }
}

November 6, 2019

Getting the Current Method Name

[Test]
public void CurrentMethodNameExperiments()
{
    var method1 = MethodBase.GetCurrentMethod().DeclaringType +
                  "." + MethodBase.GetCurrentMethod().Name + "()";
    var method2 = MethodBase.GetCurrentMethod().ToString();
    var method3 = GetMethodName();
    Debug.WriteLine("Method 1: MethodBase.GetCurrentMethod().DeclaringType + " + 
        "MethodBase.GetCurrentMethod().Name");
    Debug.WriteLine("Method 2: MethodBase.GetCurrentMethod().ToString()");
    Debug.WriteLine("Method 3: Using attributes");
    Debug.WriteLine();
    Debug.WriteLine("Method 1: " + method1);
    Debug.WriteLine("Method 2: " + method2);
    Debug.WriteLine("Method 3: " + method3);
}


public string GetMethodName(
                    [CallerMemberName] string memberName = "",
                    [CallerFilePath] string sourceFilePath = "",
                    [CallerLineNumber] int sourceLineNumber = 0)
{
    string result = memberName + "() in file \"" + sourceFilePath + 
                "\" (" + sourceLineNumber + ")";
    return result;
}
Output looks like this:
Method 1: MethodBase.GetCurrentMethod().DeclaringType + MethodBase.GetCurrentMethod().Name
Method 2: MethodBase.GetCurrentMethod().ToString() 
Method 3: Using attributes
 
Method 1: ClassLibrary1.Tests.CurrentMethodNameExperiments()
Method 2: Void CurrentMethodNameExperiments()
Method 3: CurrentMethodNameExperiments() in file "...\Tests.cs" (49)