ASP.NET Core project using Docker

Visual Studio 2017 makes it very easy to start using Docker containers to run your ASP.NET Core projects. It used to be an elaborate set of command line instructions, not knowing exactly what was happing and getting stuck whenever a build, deployment or run would fail somewhere down the line.

With VS 2017 it seems like most of that behind the scenes magic is conveniently hidden and automated. That may mean that if it still fails, for some reason, it’s not easy to trace down the issue. However, under normal circumstances, this is less likely to happen.

To use Docker containers, you first need to install Docker for Windows, here: https://docs.docker.com/docker-for-windows/

During the installation, you may receive the message indicating that Hyper-V features need to be installed. The installer takes care of that, but it may take a few minutes doing so and there’s little feedback that it’s actually installing. The machine will need to reboot after installation. After the restart, check that Docker is indeed running.

image

Before you start your first new ASP.NET Core project in Visual Studio 2017, make sure you configure a shared drive in Docker. Go to Docker Settings > Shared Drives.

image

The shared drive should be the one where you normally build and deploy your projects. Now it’s time to start Visual Studio 2017. Create a new project and be sure to select the ASP.NET Core Web Application template.

image

Enabling Docker support is now as easy as checking a box.

image

That’s it! Click OK to scaffold the project. This may take a few seconds, since a basic ASP.NET web application seems to require quite a few files.

image 

Building and deploying also takes a few moments to complete, but it does work.

image

For fun, you can check the OS Version Docker is using.

ViewData[“Message”] = System.Runtime.InteropServices.RuntimeInformation.OSDescription;

image

Linux on Windows. Who would have guessed. Winking smile

Loading large solutions in Visual Studio

Visual Studio 2017 has a new option to load solutions faster. This is especially useful if your solution has several dozen projects. The option can be found under Tools > Options > Project and Solutions.

image

As you can see, Lightweight Solution load is not enabled by default. Once you switch it on, solutions files open quickly, but the underlying projects are not immediately loaded. Only when you select and open a particular project, this project will be loaded. So while you can open de solution rather quickly, there will be a small delay upon opening each project. Nonetheless, if your daily routine is working on large solutions, but only touching a few projects, this is a handy feature.

An obvious request would be to have this option on a solution level. If you work on various solutions, some large and some small, it’s less convenient to delay loading of projects on every solution. Well, that request has already been fulfilled. Check out the properties for the solution and you can select to enable or disable Lightweight load or revert to the Visual Studio setting.

image

Trying out C# 7.0

C# 7.0 has a number of new features to simplify code and increase performance. A nice new feature is types, which make it easy to have multiple results. Another is pattern matching, which simplifies code that is conditional base on the shape of data.

You can check out these features here: https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

If you want to try them out yourself, you can download and install Visual Studio 2017 RC which was made public at the Microsoft Connect() event. You can download a free version here: https://www.visualstudio.com/vs/visual-studio-2017-rc/

You will find that some new features are not immediately available. If you are trying out tuples, you will get this message.

error CS8179: Predefined type ‘System.ValueTuple`2’ is not defined or imported

This can be resolved by adding the Nuget package System.ValueTuple.

image

In the previous preview (Visual Studio “15”) it was necessary to add the following conditional compilation symbols to your project: __DEMO__ and __DEMO_EXPERIMENTAL__. But this is no longer necessary in Visual Studio 2017 RC.

Running ASP.NET 5 on Cloud9

So you want to experiment with ASP.NET 5, but you don’t have a development and hosting environment available to work with, you can use Cloud9 IDE. From Wikipedia:

“Cloud9 IDE is a freeware online integrated development environment. It supports hundreds of programming languages, including PHP, Ruby, Python, JavaScript with Node.js, and Go. It enables developers to get started with coding immediately with pre-setup workspaces, collaborate with their peers with collaborative coding features, and web development features like live preview and browser compatibility testing.

It is written almost entirely in JavaScript, and uses Node.js on the back-end. The editor component uses Ace. As of July 2014, it uses Docker containers for its workspaces, and is hosted on Google Compute Engine.”

That is a lot of Non-Microsoft technology. So would it really be possible to run Microsoft web platform on that environment? It certainly is possible, because of Microsoft’s commitment on open source, web developers have an increasing number of options to work with. In this post, you will see how easy it is to get you started with ASP.NET MVC on Linux. At no cost and no local installation. All you need is a browser.

The first thing you need to have is an account on Cloud9. Browse to https://c9.io/ and create a new account. After logging in with your account, you see a page with workspaces. The page will be mostly empty. Then follow these steps:

  1. Create a new Workspace.

  2. Fill out the fields in the form, like so:

    Workspace Name -> aspnet5 (or any name you like, but limited to lowercase and no spaces)

    Description -> any description that makes it clear to you

  3. Choose the Custom template. Cloud9 currently has no readymade template for ASP.NET development. Once ASP.NET 5 is released that may change, although nothing is announced in this respect.
  4. Click on Create Workspace, and you are automatically transferred to the IDE.

The workspace shows the folder tree on the left, the regular menu items like File, Edit and Find at the top. At the bottom, you see the bash shell where you can enter commands. You now have a working environment on Linux. You can use the commands uname -r and uname -a to get version information.

The instructions to install ASP.NET on Linux can be found here: http://docs.asp.net/en/latest/getting-started/installing-on-linux.html

Let’s follow the essentials steps necessary to get an ASP.NET MVC project up and running. That excludes some steps that are mentioned on the reference page above. E.g. the first suggested step is to install unzip and curl, but these tools are already installed.

  1. Run the following command:

    curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

    This downloads and installs DNVM, the .NET Version Manager. When it completes, you can use dnvm to get some information.

  1. Next, we need to install the .NET Execution Environment or DNX. The prerequisites are installed with this command:

sudo apt-get install libunwind8 gettext libssl-dev libcurl3-dev zlib1g libicu-dev

During installation, you need to confirm the operation. A lot of files are download and installed.

  1. Now that the prerequisites are on the machine, we can install the latest version of the .NET Core CLR.

    dnvm upgrade -r coreclr

At the moment of writing this, the version of .NET Core is beta8. You can also use mono, which is more mature than .NET Core, but we skip this for now.

  1. To run an ASP.NET website on Linux, you can use Kestrel, a cross platform HTTP server. For Kestrel to work, we need to install libuv first. Libuv is a multi-platform asynchronous IO library that is used by Kestrel. To build libuv you should run the following commands in sequence:

    sudo apt-get install make automake libtool curl    > not necessary

    curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | sudo tar zxfv – -C /usr/local/src

    cd /usr/local/src/libuv-1.4.2

    sudo sh autogen.sh

    sudo ./configure

    sudo make

    sudo make install

    sudo rm -rf /usr/local/src/libuv-1.4.2 && cd ~/

    sudo ldconfig

You should now have .NET working on Linux. You can check the version using dnvm list.

  1. Now, let’s go back to our workspace folder, and create a folder for our ASP.NET project.

    cd workspace/

    mkdir wwwroot

Now that the working environment is setup, we can start with our ASP.NET MVC project. You can start with Visual Studio on a local machine and create a project based on a template. But since we want to use the browser only, we are going to create the necessary files “by hand”. To keep the instructions readable, when you need to add a folder, right-click on the parent folder and choose New Folder. The same applies to creating a new file and choose New File.

Here we go.

  1. Create a new file with the following content

    {

    “sdk”: {

    “version”: “1.0.0-beta8”

    }

    }

    Save this file as global.json in the wwwroot folder.

  1. Add another file, with the following content.

    {

    “version”: “1.0.0-beta8”,

    “compilationOptions”: {

    “emitEntryPoint”: true

    },

    “dependencies”: {

    “Microsoft.AspNet.IISPlatformHandler”: “1.0.0-*”,

    “Microsoft.AspNet.Server.Kestrel”: “1.0.0-*”,

    “Microsoft.AspNet.Mvc”: “6.0.0-*”,

    “Microsoft.AspNet.Mvc.TagHelpers”: “6.0.0-*”,

    “Microsoft.AspNet.StaticFiles”: “1.0.0-*”,

    “Microsoft.AspNet.Tooling.Razor”: “1.0.0-*”,

    “Microsoft.Framework.Configuration.Json”: “1.0.0-*”,

    “Microsoft.Framework.Logging”: “1.0.0-*”,

    “Microsoft.Framework.Logging.Console”: “1.0.0-*”,

    “Microsoft.Framework.Runtime.Abstractions”: “1.0.0-*”,

    “Microsoft.AspNet.Diagnostics”: “1.0.0-*”

    },

    “commands”: {

    “kestrel”: “Microsoft.AspNet.Hosting –server Microsoft.AspNet.Server.Kestrel –config hosting.json”

    },

    “frameworks”: {

    “dnxcore50”: { }

    }

    }

    Save this file as project.json.

  1. Note that the project file contains the option “emitEntryPoint”: true. However, the build that is available at time of writing this post does not seem to support this. So we will need to add a static void Main method in our Startup.cs. Add the following content to a new file, named Startup.cs.

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Threading.Tasks;

    using Microsoft.AspNet.Builder;

    using Microsoft.AspNet.Diagnostics;

    using Microsoft.AspNet.Hosting;

    using Microsoft.AspNet.Http;

    using Microsoft.AspNet.Routing;

    using Microsoft.Framework.Configuration;

    using Microsoft.Framework.DependencyInjection;

    using Microsoft.Framework.Logging;

    using Microsoft.Framework.Logging.Console;

    using Microsoft.Dnx.Runtime;

    namespace WebApplication1

    {

    public class Startup

    {

    public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)

    {

    // Setup configuration sources.

    var builder = new ConfigurationBuilder()

    .AddJsonFile(“config.json”)

    .AddEnvironmentVariables();

    Configuration = builder.Build();

    }

    public IConfiguration Configuration { get; set; }

    // This method gets called by the runtime.

    public void ConfigureServices(IServiceCollection services)

    {

    // Add MVC services to the services container.

    services.AddMvc();

    }

    // Configure is called after ConfigureServices is called.

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

    {

    loggerFactory.MinimumLevel = LogLevel.Information;

    loggerFactory.AddConsole();

    // Add static files to the request pipeline.

    app.UseStaticFiles();

    // Add MVC to the request pipeline.

    app.UseMvc(routes =>

    {

    routes.MapRoute(

    name: “default”,

    template: “{controller=Home}/{action=Index}/{id?}”);

    // Uncomment the following line to add a route for porting Web API 2 controllers.

    // routes.MapWebApiRoute(“DefaultApi”, “api/{controller}/{id?}”);

    });

    }

    public static void Main(string[] args)

    {

    }

    }

    }

  1. Add the following new folders:

    Controllers/Home

    Controllers/Views

  2. In Controllers/Home, add a new file named HomeController.cs with the following content.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using Microsoft.AspNet.Mvc;

namespace WebApplication1

{

public class HomeController : Controller

{

public IActionResult Index()

{

ViewBag.System = Environment.GetEnvironmentVariable(“_system_name”);

ViewBag.Version = Environment.GetEnvironmentVariable(“_system_version”);

return View();

}

}

}

  1. In Views/Home, add a new file named Index.cstml with the following content.

    <html>

    <body>

    <p>ASP.NET is now working on:</p>

    <p>System: @ViewBag.System</p>

    <p>Version: @ViewBag.Version</p>

    </body>

    </html>

We are almost ready. The project file (project.json) contains all dependencies for ASP.NET MVC. But these dependencies are not automatically available, we need to download these using the following command.

dnu restore

This command will download all packages.

You can now start the project using the following command.

dnx kestrel

However, when you do this, you get the following output.

Hosting environment: Production

Now listening on: http://localhost:5000

Application started. Press Ctrl+C to shut down.

This is a bit awkward because if we open this address in the browser, it will try to connect to your machine, where the site is not running. To fix this, you can either use nginx to act as a proxy. Instructions on this can be found here: http://druss.co/2015/06/asp-net-5-kestrel-nginx-web-server-on-linux/

Alternatively, you can add a hosting.json file (formerly hosting.ini) to set configuration parameters for Kestrel. In fact, we already added a reference to this file in project.json.

This hosting.json contains the following.

{

“server”:”Microsoft.AspNet.Server.Kestrel”,

“server.urls”:”http://aspnet5-sander.c9.io:8080″

}

Note that the server url contains an address. You should enter the address that is used in your workspace. The address is a combination of your username and workspace name, divided by a dash.

The port number is 8080 because by default, you cannot open up a port below 1024 on a Linux machine.

To restart the web server, press CTRL+C and run the following command.

dnx kestrel

Now you should see something like this.

Now you can open up a new browser window and navigate to

http://aspnet5-sander.c9.io/

Note that you do not need to use port 8080. Both 8080 and port 80 work and life is good.

Notes

Please note that this post uses commands an instructions that are not finalized yet. Check out https://docs.asp.net/en/latest/ for the latest information and changes.