Adventures in .NET Standard 2.0-preview1

Published by Marco on in .NET/C#

.NET Standard 2.0 is finally publicly available as a preview release. I couldn’t help myself and took a crack at converting parts of Quino to .NET Standard just to see where we stand. To keep me honest, I did all of my investigations on my MacBook Pro in MacOS.

IDEs and Tools

I installed Visual Studio for Mac, the latest JetBrains Rider EAP and .NET Standard 2.0-preview1. I already had Visual Studio Code with the C#/OmniSharp extensions installed. Everything installed easily and quickly and I was up-and-running in no time.

Armed with 3 IDEs and a powerful command line, I waded into the task.

Porting Quino to .NET Standard

Quino is an almost decade-old .NET Framework solution that has seen continuous development and improvement. It’s quite modern and well-modularized, but we still ran into considerable trouble when experimenting with .NET Core 1.1 almost a year ago. At the time, we dropped our attempts to work with .NET Core, but were encouraged when Microsoft shifted gears from the extremely low–surface-area API of .NET Core to the more inclusive though still considerably cleaned-up API of .NET Standard.

Since it’s an older solution, Quino projects use the older csproj file-format: the one where you have to whitelist the files to include. Instead of re-using these projects, I figured a good first step would be to use the dotnet command-line tool to create a new solution and projects and then copy files over. That way, I could be sure that I was really only including the code I wanted—instead of random cruft generated into the project files by previous versions of Visual Studio.

The dotnet Command

The dotnet command is really very nice and I was able to quickly build up a list of core projects in a new solution using the following commands:

  • dotnet new sln
  • dotnet new classlib -n {name}
  • dotnet add reference {../otherproject/otherproject.csproj}
  • dotnet add package {nuget-package-name}
  • dotnet clean
  • dotnet build

That’s all I’ve used so far, but it was enough to investigate this brave new world without needing an IDE. Spoiler alert: I like it very much. The API is so straightforward that I don’t even need to include descriptions for the commands above. (Right?)

Everything really seems to be coming together: even the documentation is clean, easy-to-navigate and has very quick and accurate search results.

Initial Results

  • Encodo.Core compiles (almost) without change. The only change required was to move project-description attributes that used to be in the AssemblyInfo.cs file to the project file instead (where they admittedly make much more sense). If you don’t do this, the compiler complains about “[CS0579] Duplicate ‘System.Reflection.AssemblyCompanyAttribute’ attribute” and so on.
  • Encodo.Expressions references Windows.System.Media for Color and the Colors constants. I changed those references to System.Drawing and Color, respectively—something I knew I would have to do.
  • Encodo.Connections references the .NET-Framework–only WindowsIdentity. I will have to move these references to a Encodo.Core.Windows project and move creation of the CurrentCredentials, AnonymousCredentials and UserCredentials to a factory in the IOC.
  • Quino.Meta references the .NET-Framework–only WeakEventManager. There are only two references and these are used to implement a CollectionChanged feature that is nearly unused. I will probably have to copy/implement the WeakEventManager for now until we can deprecate those events permanently.
  • Quino.Data depends on Quino.Meta.Standard, which references System.Windows.Media (again) as well as a few other things. The Quino.Meta.Standard potpourri will have to be split up.

I discovered all of these things using just VS Code and the command-line build. It was pretty easy and straightforward.

So far, porting to .NET Standard is a much more rewarding process than our previous attempt at porting to .NET Core.

The Game Plan

At this point, I had a shadow copy of a bunch of the core Quino projects with new project files as well as a handful of ad-hoc changes and commented code in the source files. While OK for investigation, this was not a viable strategy for moving forward on a port for Quino.

I want to be able to work in a branch of Quino while I further investigate the viability of:

  • Targeting parts of Quino to .Net Standard 2.0 while keeping other parts targeting the lowest version of .NET Framework that is compatible with .NET Standard 2.0 (4.6.1). This will, eventually, be only the Winform and WPF projects, which will never be supported under .NET Standard.
  • Using the new project-file format for all projects, regardless of target (which IDEs can I still use? Certainly the latest versions of Visual Studio et. al.)

To test things out, I copied the new Encodo.Core project file back to the main Quino workspace and opened the old solution in Visual Studio for Mac and JetBrains Rider.

IDE Pros and Cons

Visual Studio for Mac

Visual Studio for Mac says it’s a production release, but it stumbled right out of the gate: it failed to compile Encodo.Core even though dotnet build had compiled it without complaint from the get-go. Visual Studio for Mac claimed that OperatingSytem was not available. However, according to the documentation, Operating System is available for .NET Standard—but not in .NET Core. My theory is that Visual Studio for Mac was somehow misinterpreting my project file.

Update: After closing and re-opening the IDE, though, this problem went away and I was able to build Encodo.Core as well. Shaky, but at least it works now.

 Unfortunately, working with this IDE remained difficult. It stumbled again on the second project that I changed to .NET Standard. Encodo.Core and Encodo.Expressions both have the same framework property in their project files—<TargetFramework>netstandard2.0</TargetFramework>—but, as you can see in the screenshot to the left, both are identified as .NETStandard.Library but one has version 2.0.0-preview1-25301-01 and the other has version 1.6.1. I have no idea where there second version number is coming from—it looks like this IDE is mashing up the .NET Framework version and the .NET Standard versions. Not quite ready for primetime.

Also, the application icon is mysteriously the bog-standard MacOS-app icon instead of something more…Visual Studio-y.

JetBrains Rider EAP (April 27th)

JetBrains Rider built the assembly without complaint, just as dotnet build did on the command line. Rider doesn’t didn’t stumble as hard as Visual Studio for Mac, but it also didn’t had problems building projects after the framework had changed. On top of that, it wasn’t always so easy to figure out what to do to get the framework downloaded and installed. Rider still has a bit of a way to go before I would make it my main IDE.

I also noticed that, while Rider’s project/dependencies view accurately reflects .NET Standard projects, the “project properties” dialog shows the framework version as just “2.0”. The list of version numbers makes this look like I’m targeting .NET Framework 2.0.

Addtionally, Rider’s error messages in the build console are almost always truncated.  The image to the right is of the IDE trying to inform me that Encodo.Logging (which was still targeting .NET Framework 4.5) cannot reference Encodo.Core (which references NET Standard 2.0). If you copy/paste the message into an editor, you can see that’s what it says.[1]

Visual Studio Code

I don’t really know how to get Visual Studio Code to do much more than syntax-highlight my code and expose a terminal from which I can manually call dotnet build. They write about Roslyn integration where “[o]n startup the best matching projects are loaded automatically but you can also choose your projects manually”. While I saw that the solution was loaded and recognized, I never saw any error-highlighting in VS Code. The documentation does say that it’s “optimized for cross-platform .NET Core development” and my projects targeted .NET Standard so maybe that was the problem. At any rate, I didn’t put much time into VS Code yet.

Next Steps

  1. Convert all Quino projects to use the new project-file format and target .NET Framework. Once that’s all running with the new project-file format, it will be much easier to start targeting .NET Standard with certain parts of the framework
  2. Change the target for all projects to .NET Framework 4.6.1 to ensure compatibility with .NET Standard once I start converting projects.
  3. Convert projects to .NET Standard wherever possible. As stated above, Encodo.Core already works and there are only minor adjustments needed to be able to compile Encodo.Expressions and Quino.Meta.
  4. Continue with conversion until I can compile Quino.Schema, Quino.Data.PostgreSql, Encodo.Parsers.Antlr and Quino.Web. With this core, we’d be able to run the WebAPI server we’re building for a big customer on a Mac or a Linux box.
  5. Given this proof-of-concept, a next step would be to deploy as an OWIN server to Linux on Amazon and finally see a Quino-based application running on a much leaner OS/Web-server stack than the current Windows/IIS one.

I’ll keep you posted.[2]


[1]

Encodo.Expressions.AssemblyInfo.cs(14, 12): [CS0579] Duplicate ‘System.Reflection.AssemblyCompanyAttribute’ attribute
Microsoft.NET.Sdk.Common.targets(77, 5): [null] Project ‘/Users/marco/Projects/Encodo/quino/src/libraries/Encodo.Core/Encodo.Core.csproj’ targets ‘.NETStandard,Version=v2.0’. It cannot be referenced by a project that targets ‘.NETFramework,Version=v4.5’.

[2] Update: I investigated a bit farther and I’m having trouble using NETStandard2.0 from NETFramework462 (the Mono version on Mac). I was pretty sure that’s how it’s supposed to work, but NETFramework (any version) doesn’t seem to want to play with NETStandard right now. Visual Studio for Mac tells me that Encodo.Core (NETStandard2.0) cannot be used from Encodo.Expressions (Net462), which doesn’t seem right, but I’m not going to fight with it on this machine anymore. I’m going to try it on a fully updated Windows box next—just to remove the Mono/Mac/NETCore/Visual Studio for Mac factors from the equation. Once I’ve got things running on Windows, I’ll prepare a NETStandard project-only solution that I’ll try on the Mac.