Monthly Archives: April 2020

Version handling

Sections of your program version

Software Version is actually an interesting topic, the norm in dotNet seems to be:

Major.Minor.Build.Revision

with different opinions on what Build and Revision are. Microsoft sees Build as a recompilation of the same source but with different processor, platform or compiler change (see https://docs.microsoft.com/en-us/dotnet/api/system.version?view=netframework-4.8), which is a bit confusing because the average programmer would see Build as the Buildnumber of the CI/Buildserver system and Revision as a potential Hotfix for an existing version (see https://softwareengineering.stackexchange.com/questions/24987/what-exactly-is-the-build-number-in-major-minor-buildnumber-Revision). It is probably because Microsoft is a gigantic company which does very serious business on a scale that we cannot grasp, so I will keep Build and Revision as the average programmer sees it.

  • Major: main version, increases for major changes in the program, cannot assure backward compatibility
  • Minor: minor version, increases with new features, backward compatibility can be assumed
  • Build: increases for each Build in the CI / Build server
  • Revision: increases when implementing a hotfix for an older version without adding any new features

Program version in Visual Studio

The version of your program is defined in the AssemblyInfo.cs file. but you can also access it under Assembly Information from your project properties.

  • If Assembly version is not explictly specified, it takes the value of 0.0.0.0.
  • If File version is not explicitly specified, it takes the value of Assembly version.
  • If Product version is not explicitly specified, it takes the value of File version.

You can set a * to let VS increase your version automatically, for example 1.0.* but be careful: the wildcard in 3rd position will be the number of days since the year 2000, and the wildcard in 4th position will be the number of seconds since midnight divided by 2 (see https://stackoverflow.com/questions/826777/how-to-have-an-auto-incrementing-version-number-visual-studio). So 1.0.* is ok if you need a quick solution since the third value will always increase but it will mean nothing.

string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 
string fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; 
string productVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion;

Source: http://all-things-pure.blogspot.com/2009/09/assembly-version-file-version-product.html

The Version class

The Version class is the way to go if you want to work with versions in C#, compare them, store them, it is native and convenient.

using System;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main()
        {
            Version version1 = new Version(1, 2);
            Version version2 = new Version(1, 2, 3, 4);
            Version version3 = new Version("1.2.3.5");

            Console.WriteLine(version1.ToString());
            Console.WriteLine(version2.ToString());
            Console.WriteLine(version3.ToString());

            Console.WriteLine($"version1 < version 2 = {version1 < version2}");
            Console.WriteLine($"version2 > version 3 = {version2 > version3}");
        }
    }
}
1.2
1.2.3.4
1.2.3.5
version1 < version 2 = True
version2 > version 3 = False

Fun fact

The funny thing is that versioning is just a convention, you could do the exact opposite of everybody else and count backwards if you said this is how it works and this would be correct. But you are not a monster so keep things simple and do not confuse your poor users.

Furthermore, if you want to shine in social events with your programmer friends, tell them that each new version number of TeX, the formatting system from Donald Knuth, approaches a bit more Pi than the precedent version (you know 3.1415…). And each new version of METAFONT, the description language used to define raster fonts, also from Donald Knuth, approaches the mathematical constant e (you know, 2.7182…). The mathematician will find this very amusing but the programmer will probably have terrible nightmares.

Logging with NLog

Logging is an absolute must-have feature, which is too convenient and easy to not do it. When I start a new project it is something I will pretty much do on the first days because it brings immediate advantages:

  • for debugging
  • for commenting / understanding code
  • you have an actual document to discuss over with other departments

NLog

NLog is a flexible and free logging platform for various .NET platforms, including .NET standard. NLog makes it easy to write to several targets. (database, file, console) and change the logging configuration on-the-fly.

https://nlog-project.org/

And also very important NLog is licensed under the BSD 3-Clause, which means you can use it for free for private and commercial use on the condition that you write its copyright notice in your documentation. See https://github.com/NLog/NLog/blob/master/LICENSE.txt

In order to log with NLog you will need the following:

  1. NLog binaries
  2. Configuration file
  3. Call to the dll in source Code

See https://github.com/nlog/nlog/wiki/Tutorial

NLog Binaries

You can either add NLog over Nugget in your Visual Studio project or add a specific version of the nlog.dll in your binaries.

See https://www.nuget.org/packages/NLog

Configuration file

I like to have an external dedicated file for it, I usually call it nlog.config and store it with the binaries. Remember to configure File Properties: Copy If newer

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <targets>
    <target name="logfile"
            xsi:type="File"
            fileName="${specialfolder:folder=CommonApplicationData}/Company/Product/log/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    <target name="logconsole"
            xsi:type="Console"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    <target name="logfileNamespace"
            xsi:type="File"
            fileName="${specialfolder:folder=CommonApplicationData}/Company/Product/log/${shortdate}_Namespace.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="logconsole" />
    <logger name="*" minlevel="Info" writeTo="logfile" />
    <logger name="Company.Department.Product.Namespace" minlevel="Trace" writeTo="logfileNamespace" />
  </rules>
</nlog>

https://github.com/nlog/nlog/wiki/Configuration-file

Logging

In order to use NLog you will need to define a Log object first, this should be done in every class where you want to log. and is done with

private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

Then you will be able to call the following functions:

  • Trace() very high volume of detailed Information, used for developping
  • Debug() more than the default level of Information, used for debugging
  • Info() default level of information, typically used in the field, used for retracing program activity
  • Warn() warnings, these are expected problems that are handled by the program
  • Error() errors, these are somehow expected but the described function/feature cannot continue/fulfill its normal operation
  • Fatal() fatal error, unexpected serious error, it makes no sense to continue program execution
namespace ConsoleApp1
{
    public class Program
    {
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        public static void Main(string[] args)
        {
            Log.Info("### Starting Program ###");
            Log.Info($"Version: {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}");
            Log.Info($"Date: {System.DateTime.Now}");
            Log.Info($"OS Description: {System.Runtime.InteropServices.RuntimeInformation.OSDescription}");
            Log.Info($"OS Architecture: {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}");
        }
    }
}
2020-04-07 22:25:10.8128 INFO ### Starting Program ###
2020-04-07 22:25:10.8592 INFO Version: 1.0.0.0
2020-04-07 22:25:10.8592 INFO Date: 07.04.2020 22:25:10
2020-04-07 22:25:10.8592 INFO OS Description: Microsoft Windows 10.0.18363
2020-04-07 22:25:10.8592 INFO OS Architecture: X64