Get an email when a new release of TypeScript is available using Microsoft Flow

TypeScript evolves quickly. The TypeScript team publishes a new release every month. Some releases contain new features, some contain bug fixes. This means you have to update your version of TypeScript in your code every month if you want to be able to use the new features. But, it's very easy to miss the update news - you'll find announcements on their blog - so you don't know when you have to update TypeScript. In this post, I will show you how to get a notification as soon as a new release of TypeScript is available.

The idea is to use Microsoft Flow to send you an email, or a push notification, or a JIRA task, or a Trello card, or whatever you prefer 😃 Microsoft Flow allows to automatically do some actions when something happends. In our case we'll send an email when a new item is published in an RSS feed. The free version of Microsoft Flow includes 750 runs per month, unlimited flow creation and 15-minute checks. This is clearly enough for our need.

Creating the flow

  1. Open Microsoft Flow: https://emea.flow.microsoft.com/en-us/
  2. Click "My Flows" in the header
  3. Click "Create from blank"

  1. Set the url: https://github.com/Microsoft/TypeScript/releases.atom

  1. Click "+ New step" and "Add an action"

  1. Search for "Email notifications"

  1. Configure the action with the value from the rss item

  1. Save the flow and wait for the next TypeScript release

Result

Conclusion

Microsoft Flow and its competitors such as Zapier and IFTTT are very valuable tools. It allows you to get nice functionalities in a few minutes, while these functionalities are not provided by the original service. For instance, in this post, we have added the "send an email when a release is created in GitHub" functionality to GitHub. This is possible because GitHub implements standard functionalities such as providing an RSS feed.

As a developer, you cannot implement all the things your users want. Even if you could, the user interface would be a big mess. However, you can add standard and very easy to implement functionalities. For instance, adding an RSS feed does not require a lot of time. However, it allows hundreds of scenario thanks to Microsoft Flow or Zapier. You should really look at the available actions/triggers and see what you can add in your application to open new scenario for free for your users.

Using SSH on Windows

When you have to manage a Linux server from Windows, you need to install a SSH client. In the past, you would need to install Putty. But today, things have changed. There are 2 differents ways of using SSH on Windows.

Method 1: Using the new SSH Client feature (beta)

In the latest release of Windows 10 (Fall Creators Update 10.0.16299), 2 new optional features have been added: a SSH client and a SSH server. These features allows to use OpenSSH client and server on Windows. This is the result of the port of OpenSSH to Win32 (GitHub).

  1. Open Manage Optional Features:

  1. Click "Add a feature"

  1. Select "OpenSSH Client (beta)" and click "Install"

  1. Restart your computer
  2. Open a command prompt and use ssh

Note that this feature is still in beta, so you may encounter some issues.

Method 2: Using Windows Subsystem for Linux (WSL)

The Windows Subsystem for Linux allows to run Linux applications directly on Windows, unmodified, and without the overhead of a virtual machine. This means you can run the SSH client on Windows.

  1. Open PowerShell as Administrator and run
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
  1. Restart your computer
  2. Run bash and follow the configuration steps
  3. Use ssh

If you have any issue configuring WSL, you can follow the Microsoft documentation

Performance: string concatenation vs String.Format vs interpolated string

After writing my previous post about interpolated strings in C#, I was wondering what is the most performant way to concatenate strings. So, I use BenchmarkDotnet to quickly get the result.

Here's the code of the benchmark:

// Full code: https://gist.github.com/meziantou/b120126088457f5f87ad200f4f7bf6b0
class Program
{
    static void Main(string[] args)
    {
        BenchmarkRunner.Run<StringBenchmark>();
    }
}

[OrderProvider(SummaryOrderPolicy.FastestToSlowest)]
[CoreJob]
[MemoryDiagnoser]
class StringBenchmark
{
    [Benchmark]
    public string StringConcatenation1() => "test" + 0 + "test0";

    [Benchmark]
    public string StringFormat1() => string.Format("test{0}{1}", 0, "test0");

    [Benchmark]
    public string StringInterpolation1() => $"test{0}{"test0"}"; // Should be the same as StringFormat

    [Benchmark]
    public string FormattableString1() => Format($"test{0}{"test0"}"); // Force the creation of a FormattableString

    // ...
    // same tests with 5, 10, 20, 50 100 arguments

    private static string Format(FormattableString fs) => fs.ToString();
}
Method Mean Error StdDev Gen 0 Allocated
StringConcatenation1 82.39 ns 1.732 ns 1.8534 ns 0.0330 104 B
StringFormat1 176.44 ns 1.034 ns 0.9674 ns 0.0329 104 B
StringInterpolation1 177.31 ns 1.157 ns 1.0824 ns 0.0329 104 B
FormattableString1 229.26 ns 4.654 ns 6.9661 ns 0.0558 176 B
StringConcatenation5 453.13 ns 2.980 ns 2.4887 ns 0.1903 600 B
FormattableString5 661.04 ns 17.338 ns 23.1458 ns 0.1621 512 B
StringFormat5 688.18 ns 7.096 ns 6.2907 ns 0.1516 480 B
StringInterpolation5 692.05 ns 3.329 ns 2.9508 ns 0.1516 480 B
StringConcatenation10 899.42 ns 15.189 ns 13.4643 ns 0.3500 1104 B
FormattableString10 1,160.01 ns 9.950 ns 8.3090 ns 0.2956 936 B
StringInterpolation10 1,317.73 ns 9.445 ns 8.3726 ns 0.2861 904 B
StringFormat10 1,330.47 ns 9.240 ns 8.6433 ns 0.2861 904 B
StringConcatenation20 1,794.73 ns 7.335 ns 6.1252 ns 0.6809 2144 B
FormattableString20 2,260.88 ns 22.249 ns 18.5790 ns 0.9003 2840 B
StringInterpolation20 2,625.90 ns 2.727 ns 1.9720 ns 0.8888 2808 B
StringFormat20 2,642.51 ns 20.843 ns 18.4764 ns 0.8888 2808 B
StringConcatenation50 4,394.86 ns 86.688 ns 106.4604 ns 1.6708 5264 B
FormattableString50 5,427.57 ns 35.227 ns 32.9509 ns 2.1973 6920 B
StringFormat50 6,313.41 ns 32.824 ns 27.4097 ns 2.1820 6888 B
StringInterpolation50 6,358.77 ns 50.488 ns 42.1595 ns 2.1820 6888 B
StringConcatenation100 9,011.67 ns 62.487 ns 58.4504 ns 3.3112 10464 B
FormattableString100 10,941.37 ns 88.319 ns 78.2928 ns 4.4098 13920 B
StringFormat100 12,721.01 ns 49.627 ns 41.4407 ns 4.4098 13888 B
StringInterpolation100 12,924.46 ns 329.898 ns 392.7199 ns 4.4098 13888 B

The 4 methods are almost equivalents. But, you can notice that string concatenation is a little faster for a very small number of arguments.

Interpolated strings: advanced usages

This blog post is part of The First C# Advent Calendar, a serie of 25 posts about C#. Be sure to check out the rest of the blog posts in the calendar!

This blog post will show you how to take advantages of the interpolated strings to do more than just a basic string concatenation. Indeed, interpolated strings are often use as an easier way to concatenate strings. For instance:

var fullname = "Gérald Barré";
var nickname = "Meziantou";

var str = fullname + " aka. " + nickname;
var str = string.Format("{0} aka. {1}", fullname, nickname);
var str = $"{fullname} aka. {nickname}"; // Interpolated string is more readable

As with string.Format, you can use a custom format using a colon to separate the value and the format:

var value = 42;
Console.WriteLine($"{value:C}"); // $42.00

Under the hood

The compiler rewrites the interpolated string to create a new FormattableString using FormattableStringFactory.Create:

object value1 = "Foo";
object value2 = "Bar";
// var str = $"Test {value1} {value2}";
var str = FormattableStringFactory.Create("Test {0} {1}", new object[] { value1, value2 });

The factory creates an instance of ConcreteFormattableString using the format and the arguments. Here's the code of the class:

class ConcreteFormattableString : FormattableString
{
    private readonly string _format;
    private readonly object[] _arguments;

    internal ConcreteFormattableString(string format, object[] arguments)
    {
        _format = format;
        _arguments = arguments;
    }

    public override string Format => _format;
    public override object[] GetArguments() => _arguments;
    public override int ArgumentCount => _arguments.Length;
    public override object GetArgument(int index) => _arguments[index];

    public override string ToString(IFormatProvider formatProvider)
    {
        return string.Format(formatProvider, _format, _arguments);
    }
}

The full code source of the factory is available on GitHub in the CoreCLR repo: FormattableStringFactory.cs, FormattableString.cs.

At the end, the ToString method will call string.Format with the arguments and the specified FormatProvider.

Specifying culture

Using the old String.Format, you can specify the culture to use to format the values:

var culture = CultureInfo.GetCultureInfo("fr-FR");
string.Format(culture, "{0:C}", 42); // 42,00 €

The interpolated string syntax doesn't provide a way to directly set the format provider. By default, it use the current culture (source). You can also use the invariant culture by using the FormattableString.Invariant method:

var value = 42;
Console.WriteLine(FormattableString.Invariant($"Value {value:C}")); // Value ¤42.00

// You can simplify the usage of Invariant with  "using static"
using static System.FormattableString;
Console.WriteLine(Invariant($"Value {value:C}")); // Value ¤42.00

If you want to use a specific culture, you'll have to implement your own method (very simple):

private static string WithCulture(CultureInfo cultureInfo, FormattableString formattableString)
{
    return formattableString.ToString(cultureInfo);
}

WithCulture(CultureInfo.GetCultureInfo("jp-JP"), $"{value:C}"); // ¥42.00
WithCulture(CultureInfo.GetCultureInfo("fr-FR"), $"{value:C}"); // 42,00 €

That's the basics. Now, let's use the FormattableString class to do some trickier things 😃

Escaping command line arguments

The first example consists in escaping the values to use them as command line arguments. The final result looks like:

var arg1 = "c:\\Program Files\\whoami.exe";
var arg2 = "Gérald Barré";
var commandLine = EscapeCommandLineArgs($"{arg1} {arg2}"); // "c:\Program Files\whoami.exe" "Gérald Barré"

First, install the NuGet package Meziantou.Framework.CommandLine (NuGet, GitHub), a package for building command lines. It follows the rules provided in the very interesting post: Everyone quotes command line arguments the wrong way.

Now, you can escape the arguments of the command line using the CommandLine class, and then call string.Format:

string EscapeCommandLineArgs(FormattableString formattableString)
{
    var args = formattableString.GetArguments()
                   .Select(arg => CommandLineBuilder.WindowsQuotedArgument(string.Format("{0}", arg)))
                   .ToArray();
    return string.Format(formattableString.Format, args);
}

This works great in most cases. But it doesn't respect the format of the arguments. For instance, if the string is $"{0:C}", the C format is forgotten. A better way is to create a custom class that implements IFormatProvider. The Format method of the formatter is called once for each argument with their value and format. This, you can process them and output the value with the actual format. The code is a little longer, but it respects the formatting of the arguments:

string EscapeCommandLineArgs(FormattableString formattableString)
{
    return formattableString.ToString(new CommandLineFormatProvider());
}

class CommandLineFormatProvider : IFormatProvider
{
    public object GetFormat(Type formatType)
    {
        if (typeof(ICustomFormatter).IsAssignableFrom(formatType))
            return new CommandLineFormatter();

        return null;
    }

    private class CommandLineFormatter : ICustomFormatter
    {
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            if (arg == null)
                return string.Empty;

            if (arg is string str)
                return CommandLineBuilder.WindowsQuotedArgument(str);

            if (arg is IFormattable) // Format the argument before escaping the value
                return CommandLineBuilder.WindowsQuotedArgument(((IFormattable)arg).ToString(format, CultureInfo.InvariantCulture));

            return CommandLineBuilder.WindowsQuotedArgument(arg.ToString());
        }
    }
}

Executing a SQL query with parameters

Now, let's see how to use interpolated strings to create a parameterized query. Using parameterized query is important for security, as it's a way to protect from SQL injection attacks, and for performance.

The idea is to replace arguments by @p0, @p1 and so on to create the sql query. Then, you can create the command parameters with the actual values.

using (var sqlConnection = new SqlConnection())
{
    sqlConnection.Open();
    ExecuteNonQuery(sqlConnection, $@"
UPDATE Customers
SET Name = {"Meziantou"}
WHERE Id = {1}");
}
void ExecuteNonQuery(DbConnection connection, FormattableString formattableString)
{
    using (var command = connection.CreateCommand())
    {
        // Replace values by @p0, @p1, @p2, ....
        var args = Enumerable.Range(0, formattableString.ArgumentCount).Select(i => (object)("@p" + i)).ToArray();

        command.CommandType = System.Data.CommandType.Text;
        command.CommandText = string.Format(formattableString.Format, args);

        // Create parameters
        for (var i = 0; i < formattableString.ArgumentCount; i++)
        {
            var arg = formattableString.GetArgument(i);
            var p = command.CreateParameter();
            p.ParameterName = "@p" + i;
            p.Value = arg;
            command.Parameters.Add(p);
        }

        // Execute the command
        command.ExecuteNonQuery();
    }
}

Conclusion

Interpolated string is a very nice feature introduced in C# 6. It allows to use all the functionalities of string.Format but with a much nicer syntax. Taking advantages of the format provider allows to write more readable code in some scenario. For instance, you can automatically escape values, or do something totally different from concatenating strings such as executing a SQL query.

Combining modules with TypeScript

In the previous post, I showed how to use TypeScript modules to create small units of independent and reusable code. Creating small modules is very great. However, at runtime, you may want to download only 1 file instead of tens of files. The solution is to combine all your files into one.

To combine files, you can use a tool such as UglifyJS or just concat files basically. If you use AMD or System modules, you can configure TypeScript to output a single files with all your modules. Open the tsconfig.json file and set outFile:

{
    "compilerOptions": {
        "module": "system",
        "outFile": "file.js"
    }
}

The output file will contains all your modules in file.js:

System.register("math", [], function (exports_1, context_1) {
    "use strict";
    var __moduleName = context_1 && context_1.id;
    function add(a, b) {
        return a + b;
    }
    exports_1("add", add);
    return {
        setters: [],
        execute: function () {// file math.ts
        }
    };
});
System.register("main", ["math"], function (exports_2, context_2) {
    "use strict";
    var __moduleName = context_2 && context_2.id;
    var math_1;
    return {
        setters: [
            function (math_1_1) {
                math_1 = math_1_1;
            }
        ],
        execute: function () {
            console.log(math_1.add(1, 2));
        }
    };
});

Then you can include this file into your code. As we know we'll load the file, we can include it directly in the page in addition to SystemJS. Then, you still have to load the main module using SystemJS:

<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.19/system.js" integrity="sha256-HCJlhCZ21EKx0Wo6wCF+rbeBHlVlOSJccd4zTQe2TNw=" crossorigin="anonymous"></script>
<script src="file.js"></script>
<script>
// Load the main module
SystemJS.import("main");
</script>

This solution is very convenient if you don't want to set up a complex build workflow.