Update 20. November 2016:
I wrote the same post for the new .csproj format. Go here if you already use .csproj and not project.json
Update 11. May 2016:
The community standup on 10. May revealed the future of project.json, which made some people a little bit emotional.
Here is a great summary about it.
So the way this affects the content of this post is this:
-For “runtime and libraries RC2” and “Tooling Preview 1” coming in mid-May the same applies. The project.json will be the same.
-For “runtime and libraries RTM” and “Tooling Preview 2” coming by the end of June “very likely” the project.json will exists as it exists today. And xproj will be changed to csproj. So at this point there will be a project.json with a csproj next to it.
Long term the goal is to move thing from project.json to csproj. Overtime there will be a single project file format for the whole .net ecosystem. But this is long term, and nothing is published from this.
So:
-With RC2-Priview1 tooling the content of this post is 100% applicable.
-With RTM- Priview2 tooling the content of this post will be “very likely” 100% applicable. If not I will update it.
-Post RTM-Preview2 things will change, but the questions will still apply. In that phase I will update the post again.
Intro
Lately I work a lot with ASP.NET Core and .net core. This is I think the biggest fully open source project Microsoft ever did and between RC1 and RC2 they made huge changes in the execution environment, which successfully confused me in many ways. But all this is fine, since it was only an RC1, and none said that the same stuff will RTM.
In this post I summarize the most important settings in project.json on RC2 base. This is primarily a reference for myself, since I did not find a compact summary about this, but I guess it can also be useful for many other devs.
And a general advice before we dive in (this I don’t repeat every time, so keep this in mind): when you make a change in your project.json file and something does not work it makes sense to do a
dotnet restore
So..let's start!
What is project.json?
There are many docs out there about this, so just in short: this is a new file in the new project structure for .NET application (currently used for UWP apps and ASP.Net Core projects). It defines the project dependencies and other project specific settings. We will go through these settings in this post.
For our sample I toke the cli samples from here. In this post we will only use the HelloWeb folder.
How do we start this ASP.NET Core application?
First we need to load the dependencies into the machine. For this you just type:
dotnet restore
This basically does a nuget restore. And then:
dotnet run
More about this later..
Next question:
Where is the framework located on my machine?
Well, it can be anywhere, there is no predefined installation folder for coreCLR (remember with full framework this was Windows\Microsoft.NET\... If you install the dotnet CLI via the installer, then at this point it is under C:\Program Files\dotnet. This is where the dotnet.exe resides, but it can be anywhere on your machine, and you can also set it to the PATH from anywhere (same like java).
There is an important folder in here:
The "Shared" folder is the equivalent of having a .net framework on your machine. In this folder you see all the files, which make up a dotnet framework (like coreclr.dll). So this is where coreclr is.
Now let’s see the project.json file from the HelloWeb Project:
{
"buildOptions": {
"emitEntryPoint": true,
"debugType": "portable"
},
"dependencies" : {
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-*",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0-*"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"portable-net45+wp80+win8+wpa81+dnxcore50",
"portable-net451+win8"
]
}
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-*",
"imports": "portable-net45+wp80+win8+wpa81+dnxcore50"
}
},
"scripts": {
"postpublish": "dotnet publish-iis --publish-folder %
publish:OutputPath% --framework %publish:FullTargetFramework%"
}
}
Let’s talk about the “Frameworks” section: This is where you define on which framework your project will run on. The value you put here is the so called “Target Framework Moniker” (TFM). Sometimes my feeling is that there is a team at Microsoft, and the only thing they do is coming up with new names every week… joking aside, make sure you use the current values, since many of them were renamed from RC1 to RC2. Here is a very useful stackoverflow entry.
netcoreapp1.0 means that this project targets .NET Core 1.0.
How do I start this application?
As already discussed by just typing
dotnet run
If you look into the process with process explorer you see a few interesting thing:
First the process which runs is dotnet.exe and a HelloWeb.dll from the bin\Debug\netcoreapp1.0\ folder is passed as a parameter to dotnet.exe.
So when you execute dotnet run it first builds the code (if it is not built yet), puts the dlls into the bin folder and passes it as a parameter. At the beginning of this ASP.NET Core jurney Microsoft talked a lot about in memory compilation and originally they did not want to create a dll on the disk in development time. RC1 did not emit dlls on the disk at this point, but this was dropped in RC2, so there is always a DLL.
Btw. you can also just build your project (and not start it) with
dotnet build
If you put a dll as a parameter to dotnet.exe, what is the entry point of the application?
This is predefined and every application has to have a public static ...Main(…) method, and that is the entry point.
It’s also worth looking at the loaded dlls:
So coreclr.dll is loaded from C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0-rc2-3002678. That’s where the framework is installed (see above, I told you!).
The ASP.NET Core specific dlls are loaded from my .nuget folder (this is the global nuget cache) and they were downloaded when we did a dotnet restore. So in sum: the core framework comes from the shared folder of the dotnet directory, and ASP.NET Core is loaded from the nuget cache.
How to switch between coreCLR and full framework? How to run this ASP.NET Core application on full framework?
Switching between CoreCLR and full framework is a common thing I want to do… so let’s say we want to modify this project (the one in the HelloWeb folder) to run on full framework and not on CoreClr. (Ok, there is another folder (HelloWebFull), but let’s ignore it and modify HelloWeb to run on the full framework).
As we already discussed this the framework on which the project runs is defined in the framework section. The current (and hopefully the final) Target Framework Moniker (TFM) for full framework 4.51 is net451, so
Change 1:
"frameworks": {
"net451": { }
},
The other change is in the dependencies section.
Change 2:
"dependencies" : {
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-*",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
"Microsoft.NETCore.Platforms": "1.0.1-*"
},
Now with these two changes the application will run on full framework. So when you type
Dotnet run
then dotnet.exe in this case will create a child process and it will be HelloWeb.exe. Bamm! So there is an exe file again! And this is, because it is a classical .net application.
It’s also interesting to look at the loaded images of the process
As you can see mscoree.dll and stuff from Windows/Microsft.NET are loaded plus the classical native images are also loaded from the native image cache. The ASP.NET Core framework is loaded from the /bin folder, they are copied there… so this is not fake… the application (the same application!) in this case really runs on the full framework.
Of course this only works on Windows. The project with the modified project.json won’t compile on Linux, Mac or any other platform… net451 is Windows only.
How can I publish/deploy my ASP.NET Core application?
So there are two types of deployments in this new world: 1) portable applications and 2) well… not portable.
The huge benefit of the old .NET deployment was that you only had to install the framework once on the machine and it could be used by many applications. One framework installment (under windows\Microsoft.NET) shared by 10 or 20 application on a machine is a usual scenario. And Microsoft wanted to keep this. Think about Azure and the free tiers! Installing the framework with every single application would add a huge overhead. So the first option for deployment is to package the application without the framework. This is a cross platform deployment scenario, since it only contains .net dlls (with IL in it), but nothing platform specific. These are called portable applications. As we saw before, you can pass the dll with the main method to the dotnet command (on any platform.. not only on Windows) and it will start the installed runtime from the machine. (On my machine from C:\Program Files\dotnet\shared)
Ok, so:
How do I create a portable deployment package from my ASP.NET Core application?
The answer is
dotnet publish
Now in the last part we modified the project.json, so the project targets .net 4.51. Before you do a publish I would suggest to revert it to the original version and target dotnet core (otherwise you will get a classical .net application).
After dotnet publish is finished you will get your deployment package (basically a bunch of dlls) under bin\Debug\netcoreapp1.0\publish. If you look into that you see that it contains your application as a dll and its dependencies. Since the whole ASP.NET Core framework is a dependency of the application all these dlls are there, but the runtime (the clr) is not there. The whole thing is only ~3.2mb.
This way you can xcopy this directory to any machine with a crl installed on it (this also includeds Mac, Linux, etc.) and run that with
dotnet HelloWeb.dll
So this package contains everything except the runtime and suitable for scenarios where there is already a runtime installed on the machine and it is shared by multiple application. (Similar to the old deployment model, which still absolutely makes sense for many scenarios!)
All right, but they told that this is similar to java! Can I publish a really self-contained asp.net application, which also ships the framework?
Yes, you can!
First revert all the changes in project.json. That is our starting point for this.
Change1:
"Microsoft.NETCore.App": {
//"type": "platform",
"version": "1.0.0-*"
}
Remove the "type": "platform" part from "Microsoft.NETCore.App". “type”: “platform” means that this project uses the shared sdk (the runtime installed on my machine under C:\Program Files\dotnet\shared).
When you try to run (or build or publish) the application you will get an error, with something like this:
Cannot find runtime target for framework '.NETCoreApp,Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'. Possible causes:
1. The project has not been restored or restore failed - run `dotnet restore`
2. The project does not list one of 'win10-x64, win81-x64, win8-x64, win7-x64' in the 'runtimes' section.
(On other platforms instead of win-something you will see obviously different things, but the main message is the same).
So.. the second suggestion is what we need:
Change2:
Add this section to the project.json
"runtimes": {
"win10-x64": {}
},
*or the appropriate one for your platform (see error message before).
This creates a folder under Debug\netcoreapp1.0\win10-x64\publish, which is around 50mb and contains both your application code (including its dependencies with the ASP.NET Core framework) and the runtime.
On windows there is even a HelloWeb.exe (on other platforms a HelloWeb executable accordingly), which you can start without installing anything on the machine.
You can check in Process Explorer that it only depends on the dlls from that publish folder plus on some system dlls on Windows and on some system something on other platforms, so the runtime is also packaged with the application.
Resources
Scott Hunter,.NET CLI, April 21, 2016 - DevDays Latam 2016
ASP.NET Community Standup
That's it for today.. Keep in mind that this is still not RTM and things can be changed! I try to keep this post up-to-date.
Cheers,
Gergö