This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.
Title
Adventures in .NET Standard 2.0-preview1
Description
.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.
<h>IDEs and Tools</h>
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.
<h>Porting Quino to .NET Standard</h>
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 <c>dotnet</c> 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.
<h>The <c>dotnet</c> Command</h>
The <c>dotnet</c> 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:
<ul>
<c>dotnet new sln</c>
<c>dotnet new classlib -n {name}</c>
<c>dotnet add reference {../otherproject/otherproject.csproj}</c>
<c>dotnet add package {nuget-package-name}</c>
<c>dotnet clean</c>
<c>dotnet build</c>
</ul>
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 <a href="https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0">documentation</a> is clean, easy-to-navigate and has very quick and accurate search results.
<h>Initial Results</h>
<ul>
<c>Encodo.Core</c> compiles (almost) without change. The only change required was to move project-description attributes that used to be in the <c>AssemblyInfo.cs</c> file to the project file instead (where they admittedly make <i>much</i> more sense). If you don't do this, the compiler complains about <iq>[CS0579] Duplicate 'System.Reflection.AssemblyCompanyAttribute' attribute</iq> and so on.
<c>Encodo.Expressions</c> references <c>Windows.System.Media</c> for <c>Color</c> and the <c>Colors</c> constants. I changed those references to <c>System.Drawing</c> and <c>Color</c>, respectively---something I knew I would have to do.
<c>Encodo.Connections</c> references the .NET-Framework--only <c>WindowsIdentity</c>. I will have to move these references to a <c>Encodo.Core.Windows</c> project and move creation of the <c>CurrentCredentials</c>, <c>AnonymousCredentials</c> and <c>UserCredentials</c> to a factory in the IOC.
<c>Quino.Meta</c> references the .NET-Framework--only <c>WeakEventManager</c>. There are only two references and these are used to implement a <c>CollectionChanged</c> feature that is nearly unused. I will probably have to copy/implement the <c>WeakEventManager</c> for now until we can deprecate those events permanently.
<c>Quino.Data</c> depends on <c>Quino.Meta.Standard</c>, which references <c>System.Windows.Media</c> (again) as well as a few other things. The <c>Quino.Meta.Standard</c> potpourri will have to be split up.
</ul>
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.
<h>The Game Plan</h>
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:
<ul>
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.)
</ul>
To test things out, I copied the new <c>Encodo.Core</c> project file back to the main Quino workspace and opened the old solution in <i>Visual Studio for Mac</i> and <i>JetBrains Rider</i>.
<h>IDE Pros and Cons</h>
<h level="3">Visual Studio for Mac</h>
Visual Studio for Mac <i>says</i> it's a production release, but it stumbled right out of the gate: it failed to compile <c>Encodo.Core</c> even though <c>dotnet build</c> had compiled it without complaint from the get-go. Visual Studio for Mac claimed that <c>OperatingSytem</c> was not available. However, according to <a href="https://docs.microsoft.com/en-us/dotnet/api/system.operatingsystem?view=netstandard-2.0">the documentation</a>, <c>Operating System</c> is available for .NET Standard---but not in <i>.NET Core</i>. 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 <c>Encodo.Core</c> as well. Shaky, but at least it works now.
<img attachment="visual_studio_for_mac_sdk_mixup.png" align="left">Unfortunately, working with this IDE remained difficult. It stumbled again on the second project that I changed to .NET Standard. <c>Encodo.Core</c> and <c>Encodo.Expressions</c> both have the same framework property in their project files---<c><targetframework>netstandard2.0</targetframework></c>---but, as you can see in the screenshot to the left, both are identified as <i>.NETStandard.Library</i> 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.
<h level="3">JetBrains Rider EAP (April 27th)</h>
JetBrains Rider built the assembly without complaint, just as <c>dotnet build</c> 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. <img attachment="rider_error.png" align="right">The image to the right is of the IDE trying to inform me that <c>Encodo.Logging</c> (which was still targeting .NET Framework 4.5) cannot reference <c>Encodo.Core</c> (which references NET Standard 2.0). If you copy/paste the message into an editor, you can see that's what it says.<fn>
<h level="3">Visual Studio Code</h>
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 <c>dotnet build</c>. They <a href="https://code.visualstudio.com/Docs/languages/csharp">write about</a> Roslyn integration where <iq>[o]n startup the best matching projects are loaded automatically but you can also choose your projects manually</iq>. 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 <iq>optimized for cross-platform .NET Core development</iq> and my projects targeted <i>.NET Standard</i> so maybe that was the problem. At any rate, I didn't put much time into VS Code yet.
<h>Next Steps</h>
<ol>
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
Change the target for all projects to .NET Framework 4.6.1 to ensure compatibility with .NET Standard once I start converting projects.
Convert projects to .NET Standard wherever possible. As stated above, <c>Encodo.Core</c> already works and there are only minor adjustments needed to be able to compile <c>Encodo.Expressions</c> and <c>Quino.Meta</c>.
Continue with conversion until I can compile <c>Quino.Schema</c>, <c>Quino.Data.PostgreSql</c>, <c>Encodo.Parsers.Antlr</c> and <c>Quino.Web</c>. 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.
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.
</ol>
I'll keep you posted.<fn>
<hr>
<ft><error>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'.</error></ft>
<ft>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 <c>Encodo.Core</c> (NETStandard2.0) cannot be used from <c>Encodo.Expressions</c> (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.</ft>