It started as a quick evening project (HTML Pager for Bootstrap), one class library I needed for some web projects to generate Bootstrap-happy pager HTML, and it ended up in hours of chasing weird VS2015 project configuration issues. I just wanted to package Core project into nuget and use it from MVC6 Core projects and also "old" full .NET 4.6 web projects (MVC5). Soon after I started splitting projects and importing DLLs, Visual Studio complained and I just couldn't reference Core project from my MVC 5 web app!

Scott Hanselman explained similar in his blog post, but I needed the exact opposite thing!

Maybe .NET Core is in RTM, but it reminded me that tooling support is still in deep Preview 2 state where half of things just doesn't work, meaning there's no UI support for them.

Plan is to have 3 packages:

  1. Core class library, with no special dependencies to MVC, but just pager renderer class (xproj)

  2. MVC 5 html helper wrapper (full .net 4.6, csproj), dependency to MVC 5

  3. MVC 6 html helper wrapper (core, netstandard1.6, xproj), dependency to MVC 6

For 3), its easy since xproj can be referenced from another xproj, but Core project can't be referenced from 4.6 (csproj) project! The trick is to package core bits into nuget and then import that into csproj.


Core class library needs to specify both frameworks in project.json, like this:

"frameworks": {
   "netstandard1.3": {
     "dependencies": {
       "NETStandard.Library": "1.3.0"
   "net46": {
     "dependencies": { "System.Runtime": "4.0.20" }

It has two frameworks, so library can be used from .net 4.6 and Core. I'm using Func<,> and System.Runtime package has to be added to net46 project.

The problem is, you can Add Project Reference to it from other Core (xproj) project, but not from standard 4.6 project. To use it from 4.6, a package needs to be created and added through nuget. Every time I change something in the Core lib, I have to create the package and re-install it to 4.6 project.

In my case, I added small .bat file to the Core project root, which creates package into a common folder:

dotnet pack -o ../../InternalRepository

Dotnet pack command uses information from project.json to create the package, like id, title, dependency libs, etc. Convenient UI tool for managing nupkg and nuspec files is Package Manager Explorer https://github.com/NuGetPackageExplorer/NuGetPackageExplorer

InternaRepository is folder referenced by solution nuget.config, used by all projects in the solution:

    <add key="Local" value="InternalRepository" />
    <add key="NugetV3" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />

Note: if you change something in Core project, package needs to be created again and re-installed into 4.6 project. One way is updating build semver (1.0.x) and then update the package, other way is re-installing existing package with (in Package-Manager Console):
Update-Package JA.Pagination -reinstall

Why I didn't just pack Core package into nuget and used that? I wanted to have extension methods for Razor, so pager can be called with just Html.RenderPager(...), instead of Html.Raw(Pager.Build(...).Render()).

Since MVC5 and MVC6 have different HtmlHelper classes, I needed two additional projects, each one targeting different version of MVC and adding extension method to specific HtmlHelper class.

To be honest, I’m still not sure if this is the best way of managing 4.6 and Core projects in the same solution, but it’s the only thing that worked! In case somebody knows better way, I would be happy to hear about it, so please post the comment!

Full source code is available on Github.


After we successfully installed and configured XFCE on Ubuntu 14.04 and ASP.NET Core 1.0 in part 1 of this tutorial, we have to add NGINX to expose our great Hello World app to the internet. As I mentioned in part I, it’s not advisable to expose Kestrel directly, since it’s not meant to be used like that and has lot of limitation as a front-end web server: handling of multiple host names, authentication, HTTPS offloading, caching, just to mention few.

Nginx will receive requests for specific host name (we’ll point subdomain or domain to our Azure VM) and route them to internal Kestrel URL (localhost:5000 in this example). This configuration is called Reverse Proxy. Same thing can be done also with IIS Application Request and Routing module on Windows.

Point DNS to Azure VM

Let’s start with updating our DNS records. Open your DNS provider-of-choice and add CNAME record that points to Azure VM DNS name. Azure usually creates name like myvmname.westeurope.cloudapp.azure.com. Here, I created subdomain on this blog domain: http://coretest.hudosvibe.net


If you don’t want to update DNS records, then just edit you OS hosts file and point subdomain.domain.com to IP address of this Azure server!

NGINX installation

SSH into your VM or RDP into XFCE, and open bash. To install nginx type:

sudo apt-get update
sudo apt-get install nginx

If you open your browser and type Azure DNS name or subdomain into address bar, nginx welcome page should open! From within VM, you can try to open (with Midori) http://localhost, the same welcome page should open (the same web opened from my dev machine and from Ubuntu with Midori): 



NGINX Reverse proxy to Kestrel server configuration

Open your favorite editor (nano, geany) as sudo, create a new file with the content (again, change sub/domain name):

server {
    listen 80;
    listen [::]:80;

    server_name subdomain.domain.com;

    location / {
        proxy_pass http://localhost:5000;

This will forward all requests to internal Kestrel web server that’s listening on the port 5000 (it’s started with dotnet run, right?).

Save the file as /etc/nginx/sites-available/subdomain.domain.com

We have to enable this website, and that’s done by creating a link to that file in /etc/nginx/sites-enabled:

sudo ln -s /etc/nginx/sites-available/subdomain.domain.com /etc/nginx/sites-enabled/

Now open nginx configuration file

sudo nano /etc/nginx/nginx.conf, or
sudo geany /etc/nginx/nginx.conf

find and uncomment this line: server_names_hash_bucket_size 64;

After restarting nginx this reverse proxy configuration should work:

sudo service nginx restart


More details on nginx configuration can be found here, and about reverse proxy please visit this link.



Here are few tips how to create and run ASP.NET Core application under Linux (Ubuntu 14.04 LTS), on a cheap Azure VM (0.75GB RAM, 0.25 CPU cores). Windows Server requires a little bit more memory (way around that is to increase the size while you're configuring the machine, and then lower it while not working in RDP).

Goals of this exercise are:

  • Cheap Azure VM with Linux (around 10-15$/month). There are other hosting option, for example on Hetzner, for ~5€/m

  • RDP protocol, not VNC (VNC uses Jpegs to transfer screen updates, that's very slow compared to RDP protocol)

  • Some graphical UI that's not standard Ubuntu Gnome (to save memory) for basic text editing task (since I'm still struggling with linux bash text editors and copy-paste between them)

  • Dot Net Core installed, ASP.NET Core app

  • NGINX in front of kestrel, to serve as a Reverse Proxy(similar to Application Request Routing on IIS)

Luckily, there's a great blog post on how to install UI on top of Ubuntu Server, and uses XFCE:


TIP: to use bash and ssh from windows, there are few options: new native Bash on Win10, Git bash, or putty. They all have ssh.
Open bash and type this to ssh into new linux vm: ssh -l username IP/DNS name 

So please, go ahead, follow those simple steps, I'll wait here.

If you're running Azure VM, firewall needs to be configured for RDP:
open VM blade, Settings, Network Interface, Settings, Network Security Group, Settings, Inbound Security Rules. There, add rule for destination port 3389.

Also, it's useful to assign DNS name to that machine, to avoid using dynamic IP address.

You should be able to RDP into the machine now! First time I got some error with "connection error", but I tried again immediately, and was able to RDP and log in into XFCE.

We can now install two apps:

  • Midori web browser (open terminal, type sudo apt-get install midori)

  • Geany text editor (in terminal type sudo apt-get install geany. It will be installed under /usr/bin folder. If you navigate there with file browser, you can right click to it and choose Send To/Desktop Create Link). Btw, don’t expect code completion, it's just syntax highlighting. And if you feel brave enough, there’s always VIM. Purpose of installing text editor is not to develop an app on web server, but just to edit configuration files!

Now we should able to open Midori and page https://www.microsoft.com/net/core#ubuntu, and follow steps to install dotnet core. Needless to say, that worked fine for me:)

To create new asp.net app, follow this: https://aspnet-aspnet.readthedocs-hosted.com/en/latest/getting-started.html . Geany text editor can be used now to edit project.json and CS files (or Nano from bash). Check for namespaces when copy-pasting from tutorial, it's probably different from the one created by dotnet new.

Executing dotnet run will start Kestel, and sample page can be opened in Midori! Happy days!

XFCE running with Midori, Kestrel, Geany takes around 70% of 0.75GB RAM, which is OK for playing with new dotnet and even running small-medium websites in production.  

Next blog post will explain how to put Nginx in front of kestrel, since that's recommended production configuration (that's how node and other platforms are exposed to internet, there is always some reverse proxy in front of platform web server – kestrel doesn't know how to cache, authenticate request, handle https, etc)