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.

Advertisements

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}.

My ASP.NET is full of eels

When creating a multilingual ASP.NET application there are two sane options. You can either create different pages for each language. This can be done without duplicating the server code by using views in MVC or by using the same code behind file for different pages in web forms. However, in both cases you will have to duplicate your markup.

The second option is to use resources. Commonly in the form of resource files, though there are other options. Resource files are XML files, each file contains resources for one language and each resource is identified with a name. Resource values can contain binary data such as images but for localization purposes they will typically be strings.

Resource.resx
Resource.de.resx
Resource.sv-SE.resx
Resource.uz-Cyrl-UZ.resx

Above are examples of resource file names. The language of a resource file is denoted by an RFC 1766 language identifier before the files .resx extension. When .NET looks up a resource by name it will choose the most appropriate file available (e.g. using en if en-US doesn’t exist). The last resort is to use the resource file without a language identifier. This file must exist and it must contain all resource names you want to use or .NET will not find them.

Visual Studio has good support for editing resource files but for translations you might want to use a third party program. For example Zeta Resource Editor which is free. It allows you to edit several resource files at once.

Local and global resource files

ASP.NET web forms has excellent support for resource files. It uses two types of files, local resource files are bound to a specific page while global resource files are used everywhere. The resource files are put in two folders named App_LocalResources and App_GlobalResources respectively.

<asp:Literal runat="server" meta:resourcekey="Concept" />
<asp:Literal runat="server" Text="<%$ Resources: Concept.Text %>" />
<asp:Literal runat="server" Text="<%$ Resources: Company, Slogan %>" />

You can include text from your local resource files in an ASPX page implicitly. The first line in the code above, which is taken from a page called About.aspx, will look for resources named Concept.X in the local resource file called About.aspx.resx, where X is a valid attribute for the tag. In this case I have a resource named Concept.Text which will set the Text attribute of the literal tag.

The second line does the same thing as the first but looks up the resource name explicitly. The third line gets a resource named Slogan from the global resource file named Company.resx. Global resources cannot be used implicitly.

GetLocalResourceObject("Concept.Text") as string;
GetGlobalResourceObject("Company", "Slogan") as string;
HttpContext.GetLocalResourceObject("~/About.aspx", "Concept.Text") as string;
HttpContext.GetGlobalResourceObject("Company", "Slogan") as string;

You can also use the resources in your C# code. If you are in the code behind for a page you can use either of the first two lines while the last two lines can be used from anywhere. In the latter case you have to specify which page you want if you use a local resource file.

Strongly typed resources

Unfortunately, the local/global resource system doesn’t work very well in MVC. Instead you may want to use the following system. When you create new resource files in Visual Studio it will also generate a C# class with members for each resource. The class is regenerated when you rebuild the project to keep up to date with the resource file.

Resources.Support.Contact;

If you are using strongly typed resources there is no reason to use the directories for local and global resource files, although that should be possible as well, instead I suggest using a directory called Resources for all your resource files. In this case I have put a resource file called Support.resx in the Resources directory. In the example above I retrieve the resource named Contact.

@Project.Resources.Support.Help

You use the same code to get resources into the razor views. You have to include the project name. You may want to simplify the syntax by using using or something similar.

While the strongly typed resources look easy to use there are two major drawbacks. You can’t assemble resource names dynamically in the code, which can be useful sometimes. Secondly the generated classes only work with resource files, if you want to store resources some other way it won’t work. It might be worth putting together your own classes for dealing with resources in MVC.

Setting the language

The resources are pretty useless without a way to set the language. This has to be done at the start of each HTTP request. The best way to decide which language to use is to look at what the user’s browser tells us. But we also want the user to be able to override that decision.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    string[] SupportedLanguages = { "en", "sv" };
    string DefaultLanguge = "en";
    string language = DefaultLanguge;

    HttpRequest request = HttpContext.Current.Request;

    HttpCookie cookie = request.Cookies["Language"];
    if (cookie != null && SupportedLanguages.Contains(cookie.Value))
    {
        language = cookie.Value;
    }
    else if (request.UserLanguages != null)
    {
        foreach (string userLang in request.UserLanguages)
        {
            string lang = userLang;
            if (lang.Length < 2) continue;
            if (lang.Contains('-'))
                lang = lang.Substring(0, lang.IndexOf('-'));
            if (SupportedLanguages.Contains(lang))
            {
                language = lang;
                break;
            }
        }
    }

    CultureInfo culture = new CultureInfo(language);
    Thread.CurrentThread.CurrentCulture = culture;
    Thread.CurrentThread.CurrentUICulture = culture;
}

This function in Global.asax.cs is run at the start of each request (this works in both web forms and MVC). We check if we have a language cookie set. Then we check the browser’s languages. If none of those are acceptable we default to English. Finally we can set the language of the thread. The current culture determines how dates, currencies and such things are formatted. The current UI culture determines which resource files to use.

protected void SwitchToSwedish(object sender, EventArgs e)
{
    Response.Cookies["Language"].Value = "sv";
    Response.Redirect(Request.RawUrl);
}

This is the web forms code we use when the user clicks the Swedish button. We set the cookie and then tell the browser to refresh the page. This is the only way to do it since by the time we reach this function it is too late to change the language. We have to get another HTTP request to be able to do that.

That jQuery thing: Visual Studio

When creating a new web application in Visual Studio I suggest doing the following to get the best jQuery and JavaScript support.

  1. Create a directory called Scripts in the root of the web application. This will hold all JavaScript files.
  2. Download the latest jQuery version, both the minified and uncompressed versions, and put them in the scripts directory
  3. Go to the jQuery vsdoc File Generator and select the same version as you downloaded in step 2. Build the doc file, copy the output and put it in a file named jquery-x.x.x-vsdoc.js in the scripts directory (where x.x.x is the jQuery version number of course).

The last file will give full IntelliSense support for jQuery in Visual Studio. More on that later. Your scripts directory should now look something like this:

jquery-1.6.2.js
jquery-1.6.2.min.js
jquery-1.6.2-vsdoc.js

Next include the minified version in the master page.


<script src="Scripts/jquery-1.6.2.min.js" type="text/javascript"></script>

Both jQuery and IntelliSense for jQuery will now be available in all ASPX pages (if they use the master page). However, if we want IntelliSense in external JavaScript files we have to put the following line at the top:


/// <reference path="jquery-1.6.2.min.js" />

The reference tag is used to tell IntelliSense where to look for functions and objects outside of the current file. It can be used for any JavaScript file or web page. In this case Visual Studio will be smart enough to use the vsdoc version. I could reference the vsdoc version directly but I want to keep it consistent.

Note that we don’t actually use the uncompressed version of the jQuery file. I still recommend adding it, it might be needed if you have to debug into the jQuery code. When it is time to update jQuery we have to update all these references. Fortunately a bit of search and replace should make the process fairly simple.

JavaScript debugging

While we are on the subject it is worth mentioning the best way to debug JavaScript in Visual Studio. For this we need to use Internet Explorer when debugging.

  1. Right click any ASPX page in the Solution Explorer
  2. Select Browse With…
  3. Select Internet Explorer and set it as default

Now you can put break points in JavaScript code and step through it like you do with C# code. Internet Explorer also integrates better with Visual Studio in other ways. For example, if you close the IE window VS drops out of debug mode.

All of this has been tested with Microsoft Visual Web Developer 2010 Express, which is what I’m currently using.