Published by Marco on 14. May 2017 23:20:59
Updated by Marco on 8. Aug 2017 15:51:12

.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

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.

[image]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
-- netstandard2.0 -- 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 didn't stumble as hard as Visual Studio for Mac, but
it also didn't have 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. [image]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

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
  targets '.NETStandard,Version=v2.0'. It cannot be referenced by a project that
  targets '.NETFramework,Version=v4.5'.

[1] 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.