Scott Hanselman

Experimental: Reducing the size of .NET Core applications with Mono's Linker

August 29, '17 Comments [22] Posted in DotNetCore
Sponsored By

The .NET team has built a linker to reduce the size of .NET Core applications. It is built on top of the excellent and battle-tested mono linker. The Xamarin tools also use this linker so it makes sense to try it out and perhaps use it everywhere!

"In trivial cases, the linker can reduce the size of applications by 50%. The size wins may be more favorable or more moderate for larger applications. The linker removes code in your application and dependent libraries that are not reached by any code paths. It is effectively an application-specific dead code analysis." - Using the .NET IL Linker

I recently updated a 15 year old .NET 1.1 application to cross-platform .NET Core 2.0 so I thought I'd try this experimental linker on it and see the results.

The linker is a tool one can use to only ship the minimal possible IL code and metadata that a set of programs might require to run as opposed to the full libraries. It is used by the various Xamarin products to extract only the bits of code that are needed to run an application on Android, iOS and other platforms.

I'll add this line to a nuget.config in my project's folder. Note that NuGet will inherit global settings and ADD this line.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>

Then I'll add the IL Linker's NuGet package to my project with this command line command (or from Visual Studio):

dotnet add package ILLink.Tasks -v 0.1.4-preview-906439

The assemblies will automatically be "trimmed" when they are published (not built) so I'll build it twice, disabling it with a switch:

D:\github\TinyOS\OS Project>dotnet publish -c release -r win-x64 -o notlinked /p:LinkDuringPublish=false
Microsoft (R) Build Engine version 15.3 for .NET Core

TinyOSCore -> D:\github\TinyOS\OS Project\bin\Release\netcoreapp2.0\win-x64\TinyOSCore.dll
TinyOSCore -> D:\github\TinyOS\OS Project\notlinked\

D:\github\TinyOS\OS Project>dotnet publish -c release -r win-x64 -o linked
Microsoft (R) Build Engine version 15.3 for .NET Core

TinyOSCore -> D:\github\TinyOS\OS Project\bin\Release\netcoreapp2.0\win-x64\TinyOSCore.dll
TinyOSCore -> D:\github\TinyOS\OS Project\linked\

And here's the results:

image

You can also run it with  /p:ShowLinkerSizeComparison=true and get a nice table. I've trimmed the table as it's super long.

  TinyOSCore -> D:\github\TinyOS\OS Project\bin\Release\netcoreapp2.0\win-x64\TinyOSCore.dll
Before linking (B) After linking (B) Size decrease
----------- ----------- ----------- -----------
Total size of assemblies 48,025,824 16,740,056 65.14%
----------- ----------- ----------- -----------
TinyOSCore.dll 36,352 36,352 0.00%
Microsoft.Extensions.Configuration.dll 24,584 24,584 0.00%
Microsoft.Extensions.Configuration.Abstractions.dll 20,480 20,480 0.00%
Microsoft.Extensions.Configuration.Binder.dll 24,064 24,064 0.00%
Microsoft.Extensions.Configuration.FileExtensions.dll 22,528 22,528 0.00%
Microsoft.Extensions.Configuration.Json.dll 24,072 24,072 0.00%
Microsoft.Extensions.DependencyInjection.dll 46,600 46,600 0.00%
Microsoft.Extensions.DependencyInjection.Abstractions.dll 35,336 35,336 0.00%
Microsoft.Extensions.FileProviders.Abstractions.dll 17,920 17,920 0.00%
Microsoft.Extensions.FileProviders.Physical.dll 31,240 31,240 0.00%
Microsoft.Extensions.FileSystemGlobbing.dll 39,432 39,432 0.00%
Microsoft.Extensions.Options.dll 26,120 26,120 0.00%
Microsoft.Extensions.Options.ConfigurationExtensions.dll 16,904 16,904 0.00%
Microsoft.Extensions.Primitives.dll 33,800 33,800 0.00%
Newtonsoft.Json.dll 639,488 639,488 0.00%
Microsoft.CSharp.dll 1,092,096 392,192 64.09%
Microsoft.VisualBasic.dll 465,416 0 100.00%
Microsoft.Win32.Primitives.dll 18,968 4,608 75.71%
Microsoft.Win32.Registry.dll 85,008 0 100.00%
SOS.NETCore.dll 54,264 0 100.00%
System.AppContext.dll 14,336 2,560 82.14%
System.Buffers.dll 14,336 2,560 82.14%
System.Collections.Concurrent.dll 206,360 31,744 84.62%
System.Collections.Immutable.dll 2,378,264 0 100.00%
System.Collections.NonGeneric.dll 96,792 24,576 74.61%
System.Collections.Specialized.dll 88,608 15,360 82.67%
System.Collections.dll 326,664 52,224 84.01%

TinyOSCore -> D:\github\TinyOS\OS Project\bin\Release\netcoreapp2.0\win-x64\publish\

You can see in some places where there's no size decrease. That's because I'm using those assemblies completely. Some see a 100% decrease - they've been removed entirely - because I'm not using the Registry, for example. And some see a fractional decrease because I'm using some methods but not others.

You can check out the full instructions and try this yourself at https://github.com/dotnet/core/blob/master/samples/linker-instructions.md. Again, it's a work in progress.


Sponsor: Check out JetBrains Rider: a new cross-platform .NET IDE. Edit, refactor, test and debug ASP.NET, .NET Framework, .NET Core, Xamarin or Unity applications. Learn more and download a 30-day trial!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Referencing .NET Standard Assemblies from both .NET Core and .NET Framework

August 21, '17 Comments [39] Posted in DotNetCore | NuGet | Open Source
Sponsored By

Lots of .NET Projects sharing a .NET Standard LibraryI like getting great questions in email but I LOVE getting great questions in email with a complete and clear code repro (reproduction) that's in a git somewhere. Then I can just clone, build (many many bonus points for a clean build) and check out the bug.

I got a great .NET Core question and repro here https://github.com/ScarlettCode/Example. I forked it, fixed it, and submitted a PR. Here's the question and issue and today's fix.

The project has a C# library project (an assembly) that is written to the .NET Standard 2.0. You'll recall that the .NET Standard isn't a runtime or a library in itself, but rather an interface. They are saying that this library will work anywhere that the .NET Standard is supported, like Linux, Mac, and Windows.

Here's that main .NET Standard Library called "Example.Data" written in C#.

Then he had:

  • Windows Forms (WinForms) application in VB.NET using .NET "full" Framework 4.6
  • Console Application also using .NET Framework 4.6
  • Console Application using .NET Core 2.0

Each of these apps is referring to the Example.Data library. The Example.Data library then pulls in a database access library in the form of Microsoft.EntityFrameworkCore.InMemory via NuGet.

WinForms app -> Data Access library -> Some other library. A->B->C where B and C are packages from NuGet.

The .NET Core console builds and runs great. However, when the other projects are run you get this error:

Can't load
Could not load file or assembly
'Microsoft.EntityFrameworkCore, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=adb9793829ddae60'
or one of its dependencies. The system cannot find
the file specified.

Pretty low level error, right? First thing is to check the bin folder (the results of the compile) for a project that doesn't run. Looks like there's no Microsoft.EntityFrameworkCore there. Why not? It's assembly "C" downstream of "A" and "B". EntityFramework's assembly is referred to by the Example.Data assembly...but why didn't it get copied in?

The "full" Framework projects are using the older .csproj format and by default, they use package.config to manage dependencies. The newer projects can reference Packages as first-class references. So we need to tell ALL projects in this solution to manage and restore their packages as "PackageReferences."

I can open up the .csproj file for the Framework projects and add this line within the first <PropertyGroup> like this to change the restore style:

 <RestoreProjectStyle>PackageReference</RestoreProjectStyle>

As Oren wisely says:

"Using .NET Standard requires you to use PackageReference to eliminate the pain of “lots of packages” as well as properly handle transitive dependencies. While you may be able to use .NET Standard without PackageReference, I wouldn’t recommend it."

I can also change the default within VS's Package Management options here in this dialog.

 <RestoreProjectStyle>PackageReference</RestoreProjectStyle> Default Package management format

Hope this helps.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Draft - .NET Glossary Diagram

August 17, '17 Comments [55] Posted in DotNetCore
Sponsored By

I'm working on this slide as support for this excellent .NET Glossary. It's not done yet, but I'm curious for your thoughts. Every system has terms and concepts that are initially unfamiliar but make sense once you grok them.

image

Here are these concepts used in an example sentence, for context:

  • Application Framework - “Are you using the ASP.NET Core web framework for that microservice?”
  • Metapackage - “I want to install the ASP.NET Core framework; it’s a package of packages”
  • Package/NuGet - “I know there’s a NuGet package for decoding JSON.”
  • Library/Assembly - “Now, you’ll compile your source into an assembly”
  • .NET Standard – “Which version of the .NET Standard specification does your assembly target?"
    • "My Apple Watch supports .NET Standard 1.6 but my Windows 10 laptop supports 2.0 with more APIs.”
  • C#, F#, VB, etc – “Which language did you use?”
  • .NET SDK - “Did you get the developer tools?”
  • CLR/CoreCLR – “Which runtime is your app using?”
  • An implementation of .NET is a runtime along with libraries that implement a version of the .NET Standard
    • “Are you using .NET Core, .NET Framework, or Mono for this project?”
  • Platform - An operating system and some hardware (ARM, x64, etc.)
    • “Is that an ASP.NET Core app running in Docker on a Raspberry Pi?”

Constructive feedback, please. This is a draft.


Sponsor: Check out JetBrains Rider: a new cross-platform .NET IDE. Edit, refactor, test and debug ASP.NET, .NET Framework, .NET Core, Xamarin or Unity applications. Learn more and download a 30-day trial!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Exploring refit, an automatic type-safe REST library for .NET Standard

August 16, '17 Comments [21] Posted in DotNetCore | Open Source
Sponsored By

I dig everything that Paul Betts does. He's a lovely person and a prolific coder. One of his recent joints is called Refit. It's a REST library for .NET that is inspired by Square's Retrofit library. It turns your REST API into a live interface:

public interface IGitHubApi
{
[Get("/users/{user}")]
Task<User> GetUser(string user);
}

That's an interface that describes a REST API that's elsewhere. Then later you just make a RestService.For<YourInterface> and you go to town.

var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com");

var octocat = await gitHubApi.GetUser("octocat");

imageThat's lovely! It is a .NET Standard 1.4 library which means you can use it darn near everywhere. Remember that .NET Standard isn't a runtime, it's a version interface - a list of methods you can use under many different ".NETs." You can use Refit on UWP, Xamarin.*, .NET "full" Frameowrk, and .NET Core, which runs basically everywhere.

Sure, you can make your own HttpClient calls, but that's a little low level and somewhat irritating. Sure, you can look for a .NET SDK for your favorite REST interface but what if it doesn't have one? It strikes a nice balance between the low-level and the high-level.

I'll give an example and use it as a tiny exercise for Refit. I have a service that hosts a realtime feed of my blood sugar, as I'm a Type 1 Diabetic. Since I have a Continuous Glucose Meter that is attached to me and sending my sugar details to a web service called Nightscout running in Azure, I figured it'd be cool to use Refit to pull my sugar info back down with .NET.

The REST API for Nightscout is simple, but doe have a lot of options, query strings, and multiple endpoints. I can start by making a simple interface for the little bits I want now, and perhaps expand the interface later to get more.

For example, if I want my sugars, I would go

https://MYWEBSITE/api/v1/entries.json?count=10

And get back some JSON data like this:

[
{
_id: "5993c4aa8d60c09b63ba1c",
sgv: 162,
date: 1502856279000,
dateString: "2017-08-16T04:04:39.000Z",
trend: 4,
direction: "Flat",
device: "share2",
type: "sgv"
},
{
_id: "5993c37d8d60c09b93ba0b",
sgv: 162,
date: 1502855979000,
dateString: "2017-08-16T03:59:39.000Z",
trend: 4,
direction: "Flat",
device: "share2",
type: "sgv"
}
]

Where "sgv" is serum glucose value, or blood sugar.

Starting with .NET Core 2.0 and the SDK that I installed from http://dot.net, I'll first make a console app from the command line and add refit like this:

C:\users\scott\desktop\refitsugars> dotnet new console
C:\users\scott\desktop\refitsugars> dotnet add package refit

Here's my little bit of code.

  • I made an object shaped like each record. Added aliases for weirdly named stuff like "sgv"
  • COOL SIDE NOTE: I added <LangVersion>7.1</LangVersion> to my project so I could have my public static Main entry point be async. That's new as many folks have wanted to have a "public static async void Main()" equivalent.

After that it's REALLY lovely and super easy to make a quick strongly-typed REST Client in C# for pretty much anything. I could see myself easily extending this to include the whole NightScout diabetes management API without a lot of effort.

using Newtonsoft.Json;
using Refit;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace refitsugars
{
public interface INightScoutApi
{
[Get("/api/v1/entries.json?count={count}")]
Task<List<Sugar>> GetSugars(int count);
}

public class Sugar
{
[JsonProperty(PropertyName = "_id")]
public string id { get; set; }

[JsonProperty(PropertyName = "sgv")]
public int glucose { get; set; }

[JsonProperty(PropertyName = "dateString")]
public DateTime itemDate { get; set; }
public int trend { get; set; }
}

class Program
{
public static async Task Main(string[] args)
{
var nsAPI = RestService.For<INightScoutApi>("https://MYURL.azurewebsites.net");
var sugars = await nsAPI.GetSugars(3);
sugars.ForEach(x => { Console.WriteLine($"{x.itemDate.ToLocalTime()} {x.glucose} mg/dl"); });
}
}
}

And here's the result of the run.

PS C:\Users\scott\Desktop\refitsugars> dotnet run
8/15/2017 10:29:39 PM 110 mg/dl
8/15/2017 10:24:39 PM 108 mg/dl
8/15/2017 10:19:40 PM 109 mg/dl

You should definitely check out Refit. It's very easy and quite fun. The fact that it targets .NET Standard 1.4 means you can use it in nearly all your .NET projects, and it already has creative people thinking of cool ideas.


Sponsor: Check out JetBrains Rider: a new cross-platform .NET IDE. Edit, refactor, test and debug ASP.NET, .NET Framework, .NET Core, Xamarin or Unity applications. Learn more and download a 30-day trial!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

.NET and WebAssembly - Is this the future of the front-end?

August 12, '17 Comments [52] Posted in DotNetCore | Javascript | Open Source
Sponsored By

6 years ago Erik Meijer and I were talking about how JavaScript is/was an assembly language. It turned into an interesting discussion/argument (some people really didn't buy it) but it still kept happening. Currently WebAssembly world is marching forward and is supported in Chrome, Firefox, and in Development in Edge, Opera, and Safari.

"The avalanche has begun, it's too late for the pebbles to vote." - Ambassador Kosh

Today in 2017, WebAssembly is absolutely a thing and you can learn about it at http://webassembly.org. I even did a podcast on WebAssembly with Mozilla Fellow David Bryant (you really should check out my podcast, I'm very proud of it. It's good.)

The classic JavaScript TODO app, written with C# and .NET and Blazor

The image above is from Steve Sanderson's NDC presentation. He's writing the classic client-side JavaScript ToDo application...except he's writing the code in C#.

What is WebAssembly?

"WebAssembly or wasm is a low-level bytecode format for in-browser client-side scripting, evolved from JavaScript." You can easily compile to WebAssembly from C and C++ today...and more languages are jumping in to include WebAssembly as a target every day.

Since I work in open source .NET and since .NET Core 2.0 is cross-platform with an imminent release, it's worth exploring where WebAssembly fits into a .NET world.

Here's some projects I have identified that help bridge the .NET world and the WebAssembly world. I think that this is going to be THE hot space in the next 18 months.

WebAssembly for .NET

Despite its overarching name, this OSS project is meant to consume WASM binary files and execute them from within .NET assemblies. To be clear, this isn't compiling .NET languages' (C#, VB.NET, F#) into WebAssembly, this is for using WebAssembly as if it's any other piece of resuable compiled code. Got an existing WASM file you REALLY want to call from .NET? This is for that.

Interestingly, this project doesn't spin up a V8 or Chakra JavaScript engine to run WASM, instead it reads in the bytecode and converts them to .NET via System.Reflection.Emit. Interesting stuff!

Mono and WebAssembly

One of the great things happening in the larger .NET Ecosystem is that there is more than one ".NET" today. In the past, .NET was a thing that you installed on Windows and generally feared. Today, there's .NET 4.x+ on basically every Windows machine out there, there's .NET Core that runs in Docker, on Mac, Windows, and a dozen Linuxes...even Raspberry Pi, and Mono is another instance of .NET that allows you to run code in dozens of other platforms. There's multiple "instances of .NET" out there in active development.

The Mono Project has two prototypes using Mono and WebAssembly.

The first one uses the traditional full static compilation mode of Mono, this compiled both the Mono C runtime and the Mono class libraries along with the user code into WebAssembly code. It produces one large statically compiled application. You can try this fully statically compiled Hello World here. The full static compilation currently lives here.

So that's a totally statically compiled Hello World...it's all of Mono and your app into Web Assembly. They have another prototype with a difference perspective:

The second prototype compiles the Mono C runtime into web assembly, and then uses Mono’s IL interpreter to run managed code. This one is a smaller download, but comes at the expense of performance. The mixed mode execution prototype currently lives here.

Here they've got much of Mono running in Web Assembly, but your IL code is interpreted. One of the wonderful things about Computer Science - There is more than one way to do something, and they are often each awesome in their own way!

"Blazor" - Experimental UI Framework running .NET in the browser

With a similar idea as the Mono Project's second prototype, Steve Sanderson took yet another "instance of .NET," the six year old open source DotNetAnywhere (DNA) project and compiled it into Web Assembly. DNA was an interpreted .NET runtime written in portable C. It takes standard IL or CIL (Common Intermediate Language) and runs it "on resource-constrained devices where it is not possible to run a full .NET runtime (e.g. Mono)." Clever, huh? What "resource-constrained device do we have here six years later?" Why, it's the little virtual machine that could - the JavaScript VM that your browser already has, now powered by a standard bytecode format called WebAssembly.

To prove the concept, Steve compiles DotNetAnywhere to WASM but then takes it further. He's combined standard programming models that we see on the web with things like Angular, Knockoutjs, or Ember, except rather than writing your web applications' UI in JavaScript, you write in C# - a .NET language.

Here in the middle of some Razor (basically HTML with C# inline) pages, he does what looks like a call to a backend. This is C# code, but it'll run as WASM on the client side within a Blazor app.

@functions {
WeatherForecast[] forecasts;

override protected async Task InitAsync()
{
using (var client = new HttpClient())
{
var json = await client.GetStringAsync(AbsoluteUrl("/api/SampleData/WeatherForecasts"));
forecasts = JsonUtil.Deserialize<WeatherForecast[]>(json);
}
}
}

This would allow a .NET programmer to use the same data models on the client and the server - much like well-factored JavaScript should today - as well as using other .NET libraries they might be familiar or comfortable with.

Why do this insane thing? "To see how well such a framework might work, and how much anyone would care." How far could/should this go? David Fowler already has debugging working (again this is ALL prototypes) in Visual Studio Code. Don't take my word for it, watch the video as Steve presents the concept at the NDC Conference.

Blazor as a prototype has a number of people excited, and there was a Blazor Hackthon recently that produced some interesting samples including a full-blown app.

Other possibilities?

There's lots of other projects that are compiling or transpiling things to JavaScript. Could they be modified to support WebAssembly? You can take F# and compile it to JavaScript with F#'s Fable project, and some folks have asked about WebAssembly.

At this point it's clear that everyone is prototyping and hacking and enjoying themselves.

What do YOU think about WebAssembly?


Sponsor: Check out JetBrains Rider: a new cross-platform .NET IDE. Edit, refactor, test and debug ASP.NET, .NET Framework, .NET Core, Xamarin or Unity applications. Learn more and download a 30-day trial!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.