Upgrading an Existing Web API Project to ASP.NET Core 2.1 Preview 1

In late February 2018, .NET Core 2.1 Preview 1, ASP.NET Core 2.1 Preview 1, and Entity Framework 2.1 Preview 1 were announced and released to the public. According to the documentation, there are several themes influencing the feature set of the final release. Here are the themes:

· Faster Build Performance

· Close gaps in ASP.NET core and EF Core

· Improve compatibility with .NET Framework

· GDPR and Security

· Microservices and Azure

· More capable Engineering System

All of the themes listed are important to the evolution, maturity, and adoption of the platform. Starting a new project with .NET/ASP.NET Core 2.1 Preview is a simple process and follows the familiar workflow of creating a new project in Visual Studio. Upgrading an existing .NET/ASP.NET Core 1.x/2.0 to .NET/ASP.NET Core 2.1 Preview 1 is a little more involved and this walkthrough is aimed at assisting you with the process by providing a breakdown of my experience.

When I present on the topic of web development, I typically use a reference application I created that is used to demonstrate technologies, tools, and software engineering techniques to the attendees. As these topics evolve, so does the reference application. In preparation for the upcoming release of .NET/ASP.NET Core 2.1, I spent sometime recently upgrading a Web API project that is a part of the reference application.

The first step in upgrading the Web API project was to download and install the NET Core 2.1 Preview 1 SDK. You can download it here: .NET Core 2.1 Preview 1 SDK . It is a simple and painless install but there are two things to note:

  • The SDK installs side-by-side with other versions of the SDK but your default SDK will be the latest version which is the preview.  If you have problems with other projects and the preview SDK, you can force a project to use a specific version of the SDK using a global.json file as documented here.
  • The 64-bit installer version is 128MB. Not a huge download but make sure you have a decent connection to the internet.

Once I had the .NET Core 2.1 Preview 1 SDK installed, I began the process of updating the Web API project. This process is manual and involved editing several files to get everything updated. The first file I edited was the .csproj file. I changed the TargetFramework element to reference .NET Core 2.1:

Original element: <TargetFramework>netcoreapp2.0</TargetFramework>

Updated element: <TargetFramework>netcoreapp2.1</TargetFramework>

Next, I updated all Microsoft.AspNetCore and Microsoft.Extensions package reference elements to version “2.1.0-preview1-final”. Below is an example of one of the updated package references elements:

Original element: <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.3" />

Updated element: <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0-preview1-final" />

Since I was modifying the .csproj file, I thought it was a great opportunity to perform some cleanup. (This step is not required.) This Web API has existed since the .NET Core 1.0 RC days and at that time the practice was to list each of the needed packages from Microsoft.AspNetCore and Microsoft.Extensions individually. The result in this project; a .csproj file with 15+ package reference elements. Now, there is nothing technically wrong with this but later versions of ASP.NET Core offer a way to reduce the number of package reference elements in your .csproj file.

ASP.NET Core 2.0 introduced the metapackage Microsoft.AspNetCore.All. This package includes all packages supported by the ASP.NET Core Team, all Entity Framework Core packages, and any third-party package dependencies of ASP.NET Core and Entity Framework Core. All features of ASP.NET Core and Entity Framework Core are included. In addition, the default project template used this package. This approach takes advantage of the .NET Runtime Store which contains all the runtime assets needed to run ASP.NET Core 2.x applications. Since all the assets are part of the .NET Runtime Store and the assets are precompiled, no ASP.NET Core nuget packages are deployed with the application (Except in the case of self-contained deployments) and application startup time is reduced. If there is an overall concern of the deployment size, a package trimming process can be used to remove any packages that are not used and therefore not deployed. More information on package trimming can be located here: Package Trimming

ASP.NET Core 2.1 introduces a new metapackage Microsoft.AspNetCore.App. The concept is the same as in Microsoft.AspNetCore.All except the new metapackage reduces the number of dependencies on packages not own/supported by the ASP.NET and .NET teams. Only the packages deemed necessary to ensure major framework features are included. More information on the new metapackage Microsoft.AspNetCore.App can be located here: Microsoft.AspNetCore.App

To reduce the number of package reference elements in the .csproj file, I removed all package reference elements for Microsoft.AspNetCore.* and Microsoft.Extensions.* and added a single package reference element for the new metapackage. Here is the new entry:

<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview1-final" />

Here is the final .csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <AssemblyName>sregister_webapi</AssemblyName>
    <OutputType>Exe</OutputType>
    <PackageId>sregister_webapi</PackageId>    
    <PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
    <TrimUnusedDependencies>true</TrimUnusedDependencies>
  </PropertyGroup>

  <ItemGroup>
    <None Update="wwwroot\**\*">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </None>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview1-final" />    
    <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.4.0" />
    <PackageReference Include="Dapper" Version="1.50.2" />
    <PackageReference Include="Microsoft.Packaging.Tools.Trimming" Version="1.1.0-preview1-25818-01" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\sregister_core\sregister_core.csproj" />
    <ProjectReference Include="..\sregister_infrastructure\sregister_infrastructure.csproj" />
  </ItemGroup>

</Project>

The next set of changes were made in the program.cs file. Here is the before and after:

BEFORE:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;

namespace sregister_webapi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

AFTER:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace sregister_webapi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }
               
        private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

As I stated earlier, this Web API project was originally created in the .NET/ASP.NET Core 1.0 RC days and things are done differently now. As you can see, the recommended approach is to have main call a method that returns IWebHostBuilder which is configured to know which class to use during startup. Main calls the Build and Run methods (in that order) on the returned IWebHostBuilder which starts the application.

The last step in upgrading the Web API to .NET/ASP.NET Core 2.1 Preview was to modify the Startup.cs file. The main changes were:

In the ConfigurationServices method:

· Changed the call to add MVC services to services.AddMvcCore().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)

In the Configure method:

· Added app.UseHsts() – More information on this can be found here: HSTS

· Added app.UseHttpsRedirection() – This redirects HTTP traffic to HTTPS

Both changes in the Configure method were made to take advantage of the features of ASP.NET Core 2.1 Preview to secure web applications using HTTPS during development and production. A detailed explanation of the features can be found here: ASP.NET Core 2.1.0-preview1: Improvements for using HTTPS

Here is the before and after of Startup.cs:

BEFORE

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using sregister_core.Interfaces;
using sregister_infrastructure.Repositorities;
using sregister_core.Models;

namespace sregister_webapi
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            services.AddCors();
            services.AddOptions();
            services.Configure<SpeakerRegisterOptions>(Configuration.GetSection("Options"));

            // adding custom services
            services.AddTransient<ISpeakerRepository, SpeakerRepository>();
            services.AddTransient<IConferenceRepository, ConferenceRepository>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());

            app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
            {
                Authority = "http://localhost:9440",
                AllowedScopes = { "sregisterAPI" },
                RequireHttpsMetadata = false
            });
            
            app.UseMvc();
        }
    }
}

AFTER

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using sregister_core.Interfaces;
using sregister_infrastructure.Repositorities;
using sregister_core.Models;
using System;

namespace sregister_webapi
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvcCore()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddAuthorization()
                .AddJsonFormatters()
                .AddCors();

            services.AddHsts(options => {
                options.MaxAge = TimeSpan.FromDays(90);
                options.IncludeSubDomains = false;
                options.Preload = false;
            });

            services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options => {
                options.Authority = "http://localhost:9440";
                options.RequireHttpsMetadata = false;
                options.ApiName = "sregisterAPI";
                options.LegacyAudienceValidation = true;    //temporary until token service is updated
            });

            services.AddOptions();
            services.Configure<SpeakerRegisterOptions>(Configuration.GetSection("Options"));
            
            // adding custom services
            services.AddTransient<ISpeakerRepository, SpeakerRepository>();
            services.AddTransient<IConferenceRepository, ConferenceRepository>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());                   

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseMvc();
        }
    }
}

 

Finally, I was able to build and run the upgraded Web API that is now using .NET/ASP.NET Core 2.1 Preview 1. I haven’t explored any of the new features of .NET/ASP.NET Core 2.1 Preview 1 (except for the HTTPS enhancements) but I’m looking forward to leveraging what the new platform has to offer.

If you want to explore more about ASP.NET Core 2.1 Preview 1, here is information provided from the ASP.NET Team regarding the announcement of the release, new features, and upgrade steps. ASP.NET Core 2.1 Preview 1 Now Available

Keep Right’in Code!

Richard

Using Angular 2 RC5 with Visual Studio 2015 and ASP.NET Core 1.0

Lately, I have been spending time learning Angular 2.  During my study, I have seen many examples of setting up and getting started by using Visual Studio Code, WebStorm, and other excellent IDE’s.  I have also read several articles about setting up and using Angular 2 beta versions with Visual Studio 201x.  Since  I spend most of my time using Visual Studio 2015, this article will show you how to setup Angular 2 RC5 with Visual Studio 2015 and ASP.NET Core 1.0 on .NET Core 1.0.

Before you get stared, make sure you have Update 3 for Visual Studio 2015 installed and Preview 2 tooling for Microsoft .NET Core 1.0.  Once you have confirmed your installation, fire up Visual Studio 2015 and select “New Project” from the Start Page.  You will be presented with the dialog below:

New Project

Make sure you have selected the “ASP.NET Core Web Application (.NET Core)” option. Name the project and the solution, and set the location to whatever you would like.  Click “Ok” to continue.  Next you are presented with selecting what type of project template you would like to begin with.  Select “Empty”.  Authentication should be set to “No Authentication” and the “Host in the cloud” option should not be selected.  See the screenshot below:

Empty Template

Click “Ok” to continue.  After VS (Visual Studio) completes the setup you will be presented with the project readme HTML file in the editor.  If you take a look at Solution Explorer, your solution structure should look like the screenshot below:

Solution Explorer - Start

The next step in setting up ASP.NET Core 1.0 to serve Angular 2 RC5 is to configure your application to serve static files.  First, right-click on your web application in solution explorer and select “Manage NuGet Packages”.  In the NuGet Package Manager, enter “Microsoft.AspNetCore.StaticFiles” in the search window.  You will be presented with the screenshot below:

NuGet Package Manager

Make sure you have the latest stable version selected and click “Install”.  You may be asked permission to update your application as well as accept licensing terms.  Confirm to complete the installation and close the NuGet Package Manager Tab.  Finally edit the Startup.cs file in your solution.  It should look like the code below:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            app.UseFileServer();
        }
    }
}

For a more in depth understanding of serving static files with ASP.NET Core 1.0, please visit here.

Now we are ready to begin setting up our environment for Angular 2 RC5.  “Right-click” on your web application project and select “Add” then “New Item”.  You will be presented with the dialog below:

Package-JSON

Navigate to “Client-side” under .NET Core and then select “npm Configuration File” (package.json).  Click Add.  This file is used by NPM (Node Package Manager) to install required modules for our Angular 2 application.  You will be presented with the package.json file loaded in the editor.  Edit the package.json file to look like the code below:

{
  "version": "1.0.0",
  "name": "webapplication1",
  "scripts": {
    "postinstall": "typings install",
    "typings": "typings"
  },
  "dependencies": {
    "@angular/common": "2.0.0-rc.5",
    "@angular/compiler": "2.0.0-rc.5",
    "@angular/core": "2.0.0-rc.5",
    "@angular/forms": "0.3.0",
    "@angular/http": "2.0.0-rc.5",
    "@angular/platform-browser": "2.0.0-rc.5",
    "@angular/platform-browser-dynamic": "2.0.0-rc.5",
    "@angular/router": "3.0.0-rc.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.5",

    "systemjs": "0.19.27",
    "es6-shim": "^0.35.0",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "zone.js": "^0.6.12",

    "angular2-in-memory-web-api": "0.0.15",
    "jquery": "^3.1.0",
    "bootstrap": "^3.3.6"
  },
  "devDependencies": {
    "typescript": "^1.8.10",
    "gulp": "^3.9.1",
    "path": "^0.12.7",
    "gulp-clean": "^0.3.2",
    "fs": "^0.0.2",
    "gulp-concat": "^2.6.0",
    "gulp-typescript": "^2.13.1",
    "typings": "^0.8.1",
    "gulp-tsc": "^1.1.5"
  }
}

Immediately after saving the package.json file, Visual Studio will begin downloading all the dependencies listed to a folder named “node_modules” in the directory where your web application is located. (If you would like a detail explanation of these settings, you can go here.)  In addition, you will probably receive an error message stating that npm was not able to resolve Typings dependencies due to a missing “typings.json” file.  Let’s create that file now.

“Right-click” on the web application and select “Add” then “New Item”.  Again select “Client-side” on the far left and then select “JavaScript File”.  Make sure you name the file “typings.json”.  Your screen should look like the screenshot below:

Typings-JSON

Click “Add” and you will be presented with the typings.json file in the editor.  Edit the typings.json file to look like the code below:

{
  "ambientDependencies": {
    "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654",
    "jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
    "node": "registry:dt/node#4.0.0+20160509154515"
  }
}

Save the typings.json file.  If you would like an explanation of these settings, you can go here. Now if you switch to the project.json file and save it again, Visual Studio should complete the installation of the dependent modules without error.

Next we add a TypeScript JSON Configuration File. (tsconfig.json)  “Right-click” on the web application in Solution Explorer and select “Add” and then “New Item”.  Select “Client-side” on the left and “TypeScript JSON Configuration File”.  Your screen should look like the screenshot below:

TypeScript-JSON

Select “Add” and the tsconfig.json file will be loaded into the editor.  Edit the tsconfig.json to look like the code below:

{
  "compileOnSave": true,
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noEmitOnError": true,
    "noImplicitAny": false,
    "outDir": "./wwwroot/scripts"
  },
  "exclude": [
    "node_modules",
    "wwwroot",
    "typings/main",
    "typings/main.d.ts"
  ]
}

Save the file.  If you would like an explanation of these settings, you can go here.

We are going to use SystemJS to load our application and library modules.  To do so, we need to create a configuration file for SystemJS so that it can locate the code we need loaded.

Since ASP.NET Core 1.0 serves static files from the “wwwroot” folder, we are going to place the SystemJS configuration file in a folder named “scripts” under this folder.  “Right-click” on the wwwroot folder and select “Add” then “New Folder”.  Name the folder “scripts”.  Solution Explorer should look like the screenshot below:

Solution Explorer - scripts folder

Now add the SystemJS configuration file to the scripts folder by selecting “Right-click” on the script folder and selecting “Add” then “New Item”.  You will be presented with the dialog below:

SystemJS

Select “Client-side” on the left and “JavaScript File”.  Name the file “systemjs.config.js”.  Click “Add” and the “systemjs.config.js” file will be displayed in the editor.  Edit the “systemjs.config.js” file to look like the code below:

(function (global) {
    // map tells the System loader where to look for things
    var map = {
        'app': 'scripts',
        '@angular': 'libs/@angular',
        'angular2-in-memory-web-api': 'libs/angular2-in-memory-web-api',
        'rxjs': 'libs/rxjs'
    };
    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'app': { main: 'main.js', defaultExtension: 'js' },
        'rxjs': { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { defaultExtension: 'js' }
    };
    var ngPackageNames = [
      'common',
      'compiler',
      'core',
      'forms',
      'http',
      'platform-browser',
      'platform-browser-dynamic',
      'router',
      'router-deprecated',
      'upgrade'
    ];
    // Add package entries for angular packages
    ngPackageNames.forEach(function (pkgName) {
        packages['@angular/' + pkgName] = { main: './bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
    });
    var config = {
        map: map,
        packages: packages
    }
    System.config(config);
})(this);

If you are interested in the settings in the “systemjs.config.js” file, you can go here.

There is one final configuration step to complete and then we are ready to code our Angular 2 application.  As stated before, ASP.NET Core 1.0 serves static files from the wwwroot folder by default.  As a result, we need to move required library files from the node_modules folder to a location under the wwwroot folder.  In addition, if we would like to perform any debugging of the TypeScript code in our browser development tools, we need to have the original TypeScript files served from the server.  In order to accomplish this, we are going to use a gulp script to handle copying the files to their needed location.

“Right-click” on the web application and select “Add” then “New Item”.  Select “Gulp Configuration File”.  The dialog should look like the screenshot below:

Gulp

Click “Add” and you will be presented with gulpfile.js in the editor.  Edit the gulpfile.js to look like the code below:

/// <binding AfterBuild='clearLibsDestinationFolder, clearAppDestinationFolder, moveToLibs' />
/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');
var clean = require('gulp-clean');

var libsDestPath = './wwwroot/libs/';
var appDestPath = './wwwroot/app/';

//clear destination folders
gulp.task('clearLibsDestinationFolder',
    function () {
        return gulp.src(libsDestPath)
            .pipe(clean());
    });

gulp.task('clearAppDestinationFolder',
    function () {
        return gulp.src(appDestPath)
            .pipe(clean());
    });

gulp.task('moveToLibs', function () {
    gulp.src([
      'node_modules/es6-shim/es6-shim.min.js',
      'node_modules/systemjs/dist/system-polyfils.js',
      'node_modules/systemjs/dist/system.src.js',
      'node_modules/reflect-metadata/Reflect.js',
      'node_modules/rxjs/bundles/Rx.js',
      'node_modules/zone.js/dist/zone.js',
      'node_modules/jquery/dist/jquery.*js',
      'node_modules/bootstrap/dist/js/bootstrap*.js',

      'node_modules/core-js/client/shim.min.js'

      //'node_modules/systemjs/dist/*.*',
    ]).pipe(gulp.dest('./wwwroot/libs/'));

    gulp.src(['node_modules/@angular/**/*'], { base: 'node_modules/@angular' })
        .pipe(gulp.dest('./wwwroot/libs/@angular'));
    gulp.src(['node_modules/angular2-in-memory-web-api/**/*'], { base: 'node_modules/angular2-in-memory-web-api' })
        .pipe(gulp.dest('./wwwroot/libs/angular2-in-memory-web-api'));
    gulp.src(['node_modules/rxjs/**/*'], { base: 'node_modules/rxjs' })
        .pipe(gulp.dest('./wwwroot/libs/rxjs'));

    gulp.src([
      'node_modules/bootstrap/dist/css/bootstrap.css'
    ]).pipe(gulp.dest('./wwwroot/libs/css'));

    //copy typescript files for debugging purposes - would not deploy to production environment
    gulp.src(['app/**/*']).pipe(gulp.dest('./wwwroot/app'));
});

The gulp file is configured to execute after a successful build of the solution once the Task Runner Explorer is setup.  From the VS menu, select “View”, “Other Windows”, and then “Task Runner Explorer”.   Click the “Refresh” button (top left, next to the application name) and Task Runner Explorer will read the gulp file.  You should see three(3) task: clearAppDestinationFolder, clearLibsDestinationFolder, and moveToLibs.  You should also see the number three(3) next to the “After Build” Bindings.  Now the gulp script will execute after a successfully build.

Okay, finally let’s code our Angular 2 application.

First step is to add an “index.html” file.  “Right-click” on the wwwroot folder and select “Add” then “New Item” and add an HTML file named “index.html”.  Edit the file to look like the code below:

<!DOCTYPE html>
<html>
<head>
    <title>Angular 2/ASP.NET Core 1.0 QuickStart</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="libs/css/bootstrap.css">

    <!-- 1. Load libraries -->
    <script src="libs/jquery.min.js"></script>
    <script src="libs/bootstrap.min.js"></script>
    <script src="libs/zone.js"></script>
    <script src="libs/Reflect.js"></script>
    <script src="libs/system.src.js"></script>

    <!-- Polyfill(s) for older browsers -->
    <script src="libs/es6-shim.min.js"></script>
    
    <!-- 2. Configure SystemJS -->
    <script src="scripts/systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
</head>
<body>
<h1>Hello world from ASP.NET Core 1.0 on .NET Core 1.0!</h1>
    <br/><br/>
    <my-app>Loading...</my-app>
</body>
</html>

Now let’s add a new folder name “app” that will contain all of our TypeScript code for our Angular application.  “Right-click” on the web application project and select “Add” then “New Folder”.  Name the folder “app”.

The first TypeScript file we will add to the project is “main.ts”.  This is where our Angular 2 application starts up.  “Right-click” on the “app” folder and select “Add” then “New Item”.  Select “Client-side” on the left and in the center select “TypeScript File”.  Name the file “main.ts” and select “Add”.  “main.ts” should now be loaded in the editor and your solution should look similar to the screenshot below:

Main.ts

Edit the main.ts file to look like the code below:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Visual Studio will flag a few errors but that is okay for now.  They will be resolved when we add the remaining files.  Now let’s add a new TypeScript file to the app folder named “app.module.ts” and edit the file to contain the code below:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';

@NgModule({
    imports: [BrowserModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }

Finally, let’s add a third TypeScript file to the app folder named “app.component.ts” and edit the file to contain the code below:

import { Component } from '@angular/core';

@Component({
    selector: 'my-app',
    template: '<h3>Angular 2 RC5 is running here!</h3>'
})

export class AppComponent { }

After adding the third TypeScript file, all of the Visual Studio errors should have been resolved.  Your solution should look similar to the screenshot below:

Final TypeScript

I will publish another blog post with more details about the three(3) TypeScript files we just added.  If you would like more information now, you can go here.

Okay, let’s build the application in Visual Studio.  The application should build successfully.  Now let’s launch the application and you should be greeted with the screenshot below:

AppWelcome

We have an Angular 2 RC5 application served up from ASP.NET Core 1.0 on .NET Core 1.0 and built using Visual Studio 2015!  You can find a Visual Studio solution here.

I hope this post is helpful with getting started with Angular 2 development using Visual Studio 2015 and ASP.NET Core 1.0 on .NET Core 1.0.  Let me know if you have any questions.  Keep Right’in Code!

Richard - @rightincode