What’s with .NET nowadays?

I was a bit confused about the current state of .NET. So I put together this very simplified overview.

.NET Standard Library
The formal specification for .NET. Specifies a minimal set of APIs for a .NET implementation.

.NET Framework
The old .NET implementation from Microsoft. Proprietary license. For Windows only.

Mono (Xamarin)
A third party implementation of .NET. Open source. Cross-platform.

.NET Core
The new .NET implementation from Microsoft. Open source. Cross-platform.

ASP.NET
The old set of web frameworks for .NET. Open source. Runs on .NET Framework and Mono.

ASP.NET Core
The new set of web frameworks for .NET. Open source. Runs on .NET Framework and .NET Core.

Windows Forms
The old technology for desktop applications for Windows. Runs on .NET Framework.

Windows Presentation Foundation
The newer technology for desktop applications for Windows. Runs on .NET Framework.

Universal Windows Platform
The newest technology for applications for Windows and Windows Phone. Runs on .NET Framework and .NET Core.

Visual Studio
The IDE for .NET development. Proprietary license. For Windows only.

Visual Studio Code
An editor for .NET development. Open source. Cross-platform.

Regular expressions in \.NET

The String class in .NET provides plenty of methods for dealing with text. But when they fall short we can turn to the Regex class.

bool valid = Regex.IsMatch(identifier, @"^[a-z][a-z0-9]*$");

string episode = Regex.Match(filename, @"S\d{2}E\d{2}").Groups[0].Value;

Match match = Regex.Match(url, @"^http://(?<domain>[^:]+):(?<port>[0-9]+)");
if (match.Success)
{
    string domain = match.Groups["domain"].Value;
    string port = match.Groups["port"].Value;
}

MatchCollection matches = Regex.Matches(users, @" (\w+),?");
foreach (Match match in matches)
{
    string user = match.Groups[1].Value;
}

Match match = Regex.Match(users, @"Users: ((\w+)(, )?)*");
foreach (Capture capture in match.Groups[2].Captures)
{
    string user = capture.Value;
}

The syntax for regular expressions in .NET is fairly standard. Character classes uses Unicode unless you pass the ECMAScript flag. Remember to escape backslashes or use verbatim string literals. The last example uses captures, which is a way to get history when multiple matches are made.

Regex regex = new Regex(@"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (.+)$",
    RegexOptions.ECMAScript | RegexOptions.Compiled);

foreach (string line in log)
{
    Match match = regex.Match(line);
    if (!match.Success) { continue; }
    string message = match.Groups[1].Value;
}

The Regex methods can be called statically, like in the earlier examples, or the class can be instantiated as in the latter example. Since a regular expression have to be processed before it can be used it makes sense to reuse it. However, when used statically the Regex class uses an internal cache, so the static usage isn’t as bad as it may seem.

Regex objects can also be processed further by passing the Compiled flag. The regular expression is then turned into an assembly. This makes string matching faster but comes at a significant startup and memory cost. You can even go all out and turn a regular expression into a permanent assembly.

Regular expressions in Visual Studio

image

As useful as regular expressions are in code it can be even more useful as a tool when writing code. But for reasons unknown the syntax used in Visual Studio is very different from that in .NET. For example, tags are enclosed with { } instead of ( ), character classes are prefixed with : instead of \ and repetitions are expressed as ^n instead of {n}.

LINQ: I only speak in SQL

LINQ to SQL (Language Integrated Query to Structured Query Language) is probably the best known use of LINQ. It gives us an easy to use way of communicating with SQL databases.

CREATE TABLE People (
  Id INT IDENTITY,
  Age INT NULL,
  Name VARCHAR(50) NULL
);

First we create a simple table in our database. Out of the box LINQ to SQL only supports SQL Server but you can use LINQ to DataSet or other LINQ providers to get around this.

[Table(Name = "People")]
public class Person
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    public int Id;
    [Column]
    public int Age;
    [Column]
    public string Name;
}

In the code we then create a class that the table can be mapped against. We use attributes to tell LINQ that the class represents a table. In this case we also tell it the table name since it’s different from the class name. It makes sense to name tables in plural and classes in singular. In order for LINQ to work properly the table must have a primary key, which in this case is generated by the database.

Visual Studio can generate this class for us, or it can generate the database from the class or even both of them from a schema. In this case I have created both the class and the table manually.

DataContext database = new DataContext(
    @"Data Source     = 127.0.0.1,15096;
      Network Library = DBMSSOCN;
      Initial Catalog = linqtest;
      User Id         = sa;
      Password        = hunter2;");
Table<Person> people = database.GetTable<Person>();

Before we can use the database we must connect to it. We do this by creating a new DataContext and giving it a connection string. The DataContext takes care of connecting to the database as needed. It also keeps track of changes which we will come to later.

We must also create a table object in order to have something to query against. In practice you should create a strongly typed DataContext instead. That is, a class which inherits from DataContext and declares all tables as public fields.

from p in people select p.Age + " " + p.Name
"28 Edgar", "34 Marit", "14 Trulf"
from p in people orderby p.Age select p.Age + " " + p.Name
"14 Trulf", "28 Edgar", "34 Marit"
people.Where(p => p.Age > 20).Select(p => p.Age + " " + p.Name)
"28 Edgar", "34 Marit"

We can now use LINQ to run queries against the table. These queries are translated to expression trees which are in turn translated to SQL and run against the database.

foreach (Person p in people)
    p.Age++;
database.SubmitChanges();

from p in people orderby p.Age select p.Age + " " + p.Name
"15 Trulf", "29 Edgar", "35 Marit"

Part of the DataContext’s responsibilities is to keep track of changes to objects that correspond to table rows. Because of this we can make our changes permanent by submitting them to the database.

Person alan = new Person() { Age = 41, Name = "Alan" };
people.InsertOnSubmit(alan);
Person marit = people.Single(p => p.Name == "Marit");
people.DeleteOnSubmit(marit);
database.SubmitChanges();

people.Select(p => p.Name)
"Edgar", "Trulf", "Alan"

Naturally, we can also add or remove rows from the table. This also requires that we submit our changes. If you create associations to other tables additions and removals will be tracked automatically.

LINQ: from one IEnumerable to another

LINQ (Language Integrated Query) is a .NET framework which adds three things. A set of classes for use with IEnumerable, a new query syntax and a new IQueryable interface. The last one extends IEnumerable with the ability to run data queries. This is used by LINQ to SQL and LINQ to XML. But in this blog post I will focus on LINQ to Objects.

List<int> integers = new List<int> { 1, 2, 5, 7, 2, 9, 2 };
IEnumerable sorted = integers.OrderBy(i => i);
sorted;
1, 2, 2, 2, 5, 7, 9
integers.Add(4);
sorted;
1, 2, 2, 2, 4, 5, 7, 9
integers.Add(5);
sorted;
1, 2, 2, 2, 4, 5, 5, 7, 9

The first thing to learn is that LINQ methods does not return a list. What it returns is an object that knows what the data source is and knows what to do with it. This means that LINQ can, and will, run the query against the data source every time the result is requested. The example above shows what this means in practice. If you do not want this behavior you can convert the result to something permanent with ToList or ToArray.

from i in integers select i;
1, 2, 5, 7, 2, 9, 2, 4, 5
from i in integers select i + 2;
3, 4, 7, 9, 4, 11, 4, 6, 7
integers.Select(i => i + 3);
4, 5, 8, 10, 5, 12, 5, 7, 8
integers.Select((i, index) => index + ":" + i);
"0:1", "1:2", "2:5", "3:7", "4:2", "5:9", "6:2", "7:4", "8:5"

The LINQ query syntax as shown in the two first examples above is probably the most noticeable part of LINQ. However, it’s only syntactic sugar and hence it’s not a .NET extension but a language extension. Every query written in this new way can be written with methods as shown in the third example. There are LINQ methods that cannot be used via the query syntax.

The query syntax looks like SQL but notice that the select clause comes last. From and select are the only required clauses. You can use select to map a set of objects to a new set of objects.

from i in integers where i < 5 select i;
1, 2, 2, 2, 4
integers.Where(delegate(int i) { return i >= 5; });
5, 7, 9, 5
integers.Where((i, index) => i > index);
1, 2, 5, 7, 9

The where clause is used to filter the result set. You may have noticed the => syntax by now. This is the new lambda expression syntax which can be used wherever you used to use delegates before. I showed that you can still use delegates if you want to in the second example. There is however an important difference. Lambda expressions can be turned into expression trees which LINQ can translate into something else, for example SQL. This is how you can write LINQ queries in C# against a database. Hence you can’t use delegates with LINQ to SQL (which is hardly a loss).

from i in integers orderby i select i;
1, 2, 2, 2, 4, 5, 5, 7, 9
from i in integers orderby i / 3 descending, i select i;
9, 7, 4, 5, 5, 1, 2, 2, 2
integers.OrderByDescending(i => i / 3).ThenBy(i => i);
9, 7, 4, 5, 5, 1, 2, 2, 2

Orderby is used to order the set. You can order by multiple conditions as seen in the last two examples. Notice the use of ThenBy which is required, if OrderBy is used the whole set will be reordered and the first condition forgotten. The last example also shows chaining which is very useful in LINQ when you’re not using the query syntax.

from i in integers group i by i / 3 into g let m = g.Key * 3 select
  String.Format("{0}-{1}:{2}", m, m + 2, g.Count());
"0-2:4", "3-5:3", "6-8:1", "9-11:1"

You can use group to group elements like in SQL. This example also shows let which you can use to introduce a new variable inside the query. Here I use it so I don’t have to multiply by three twice.

List<KeyValuePair<int, char>> characters = new List<KeyValuePair<int, char>> {
  new KeyValuePair<int, char>(1, 'a'),
  new KeyValuePair<int, char>(2, 'b'),
  new KeyValuePair<int, char>(3, 'c'),
  new KeyValuePair<int, char>(4, 'd'),
  new KeyValuePair<int, char>(5, 'e')};
from c in characters select c.Key + "=" + c.Value;
"1=a", "2=b", "3=c", "4=d", "5=e"

from i in integers from c in characters where i == c.Key select c.Value;
'a', 'b', 'e', 'b', 'b', 'd', 'e'
from i in integers join c in characters on i equals c.Key select c.Value;
'a', 'b', 'e', 'b', 'b', 'd', 'e'
from i in integers
  join c in characters on i equals c.Key into n
  from k in n.DefaultIfEmpty(new KeyValuePair<int, char>(0, '_'))
  select i + "=" + k.Value;
"1=a", "2=b", "5=e", "7=_", "2=b", "9=_", "2=b", "4=d", "5=e"

Of course you can also use joins. If you use multiple from you get a cross join. A join on equals gives an inner join. In my example, where I use a where clause for the cross join, these give the same result. In SQL the cross join is highly discouraged so I would suggest not using it here either. The last example shows how to do an left outer join.

There are several other methods which I haven’t shown here, such as Single, Union, Count, Distinct etc. Or you can create your own if you feel something is missing.