Add Angular to MVC – Part 2

In the first part of this blog series, you added Angular 2 to an MVC application using Visual Studio. In this blog post, you will learn how to add a Web API that can be called from an Angular service. You will modify the Global.asax to automatically convert pascal-cased properties in C# classes into camel-cased TypeScript properties. You will build an Angular service, learn to inject it into a component, then call the service to retrieve product data. Finally, you will take the returned data and build an HTML table. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.


Figure 1: The product list page written using Angular

Open up the project you created in the last blog post and follow along with the steps outlined in this one to create the final project. 

Create Web API

To retrieve data from an Angular application, or any client-side technology, you create a Web API. Right mouse click on the \Controllers folder and select Add | Web API Controller Class (v2.1) from the menu. Set the name to ProductApiController and click the OK button.

Delete all the code within this new controller class as you are going to write your Web API methods using a more updated approach than what is generated by Visual Studio. Type in the code listed in the code snippet below to create a method that retrieves all products from the mock data returned from the ProductViewModel class.

public IHttpActionResult Get() {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  vm.LoadProducts();
  if (vm.Products.Count() > 0) {
    ret = Ok(vm.Products);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add WebApiConfig Class

If you did not have the Web API in your MVC application before, then you need to add a WebApiConfig class to your project. Look in the the \App_Start folder and see if the WebApiConfig.cs class exists. If not, right mouse click on the \App_Start folder and add a new class. Set the name to WebApiConfig and click the OK button. Make the class file look like the following:

using System.Web.Http;
namespace PTC
{
  public static class WebApiConfig
  {
    public static void Register(HttpConfiguration config) {
      // Web API configuration and services
      // Web API routes
      config.MapHttpAttributeRoutes();
      config.Routes.MapHttpRoute(
          name: "DefaultApi",
          routeTemplate: "api/{controller}/{id}",
          defaults: new { id = RouteParameter.Optional }
      );
    }
  }
}

This code adds a new route template to the routes accepted by this MVC application. Any call that starts with “api” is assumed to be a Web API call and thus the ASP.NET engine knows to look for an class with the specified controller name, and that inherits from the ApiController class.

Fix the Global.asax

If you open the \Models\Product class you see a standard C# class definition. As you can imagine, you are going to have a similar class on the client-side that maps all properties one-to-one from this C# class to the TypeScript class. However, the C# class uses pascal-casing of properties, while our TypeScript class is going to use camel-casing. You can add code to the Application_Start method in the Global.asax file to automatically convert properties from pascal-case to camel-case. Open the Global.asax file and at the top of the file add the three using statements shown in this code snippet.

using Newtonsoft.Json.Serialization;
using System.Web.Http;
using System.Net.Http.Formatting;

Add a single line of code to the Application_Start method to call the WebApiConfig.Register() method you just wrote. Place this line of code before you call the RouteConfig.RegisterRoutes() method.

protected void Application_Start() {
  AreaRegistration.RegisterAllAreas();
  FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
  // Add Web API
  GlobalConfiguration.Configure(WebApiConfig.Register);
  RouteConfig.RegisterRoutes(RouteTable.Routes);
  BundleConfig.RegisterBundles(BundleTable.Bundles);      
}

Next, add code after all the registration calls to perform the pascal-case to camel-case conversion for you.

// Get Global Configuration
HttpConfiguration config = GlobalConfiguration.Configuration;
// Convert to camelCase
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

The above code selects the current GlobalConfiguration.Configuration property and assigns it a variable called config. The next line of code queries the Formatters collection and retrieves the first instance of a JsonMediaTypeFormatter object it finds. Finally, you create a new instance of a CamelCasePropertyNamesContractResolver and assign that to the formatter’s ContractResolver property. This property controls how the JSON objects are formatted and sent to the client-side caller.

Build Client-Side Product List

Now that you have your server-side pieces in place, and have gotten Angular to work in your project, you can start building the HTML and classes to create a list of products. There are two new files you are going to create in this part of the article. One is a Product class and the other is a Product Service class.

Create Product Class

As you are going to retrieve a collection of Product classes from the Web API, you need a Product class written in TypeScript. Right mouse click on the \app\product folder and select Add | TypeScript file from the context-sensitive menu. Give this new file the name of product.ts. Add the following code in this file.

export class Product {
productId: number;
productName: string;
introductionDate: Date;
price: number;
url: string;
categoryId: number;
}

Notice that the structure of this class is exactly like the Product class that the Entity Framework generated. The only difference is the use of camel-case instead of pascal-case. But, as you remember we added code in the Global.asax to handle this conversion for us.

Create Product Service

We need an Angular product service to call the Web API controller you created earlier. This product service will retrieve all products. Right mouse click on the \app\products folder and select New | TypeScript file from the menu. Set the name to product.service.ts and click the OK button. Add the code shown in Listing 1.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { Product } from "./product";
@Injectable()
export class ProductService {
private url = "api/productApi";
constructor(private http: Http) {
}
getProducts(): Observable<Product[]> {
return this.http.get(this.url).map(this.extractData).catch(this.handleErrors);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
  private handleErrors(error: any): Observable<any> {
    let errors: string[] = [];
    switch (error.status) {
      case 404: // Not Found
        errors.push("No Product Data Is Available.");
        break;
      case 500: // Internal Error
        errors.push(error.json().exceptionMessage);
        break;
      default:
        errors.push("Status: " + error.status 
                    + " - Error Message: " 
                    + error.statusText);
        break;
    };
    console.error('An error occurred', errors);
    return Observable.throw(errors);
  }
}

Listing 1: The product service retrieves a collection of products from your Web API.

Let’s break down the code shown in Listing 1. You first import the various classes and functions you need for this service using the ‘import’ keyword.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { Product } from "./product";

The injectable class is needed to support the use of the @Injectable decorator. This decorator informs Angular that this class may be injected into the constructor of any component. The Http and Response classes are used to access the Web API and to retrieve the response from the Web API. Next is the Observable extension function supplied by RxJS or Reactive Extensions for JavaScript. This is a library that helps you transform, compose and query any type of data. In the ProductService class, you use it to take the result set of data you retrieve from the Web API and turn it into an observable collection of data. Next are a series of different reactive extensions to support the map, catch and throw functions you use in this class. The last import is the Product class you created. Since you want to return an array of product objects returned from the Web API, you must import this class.

Angular will inject the Http service into our ProductService class. Any class marked with the @Injectable decorator may be injected in any class by declaring it in the constructor.

constructor(private http: Http) {
}

The next two functions in our class are getProducts and extractData. The getProducts function calls our Web API using the get function of the http service. It creates an observable array of products using the map extension function. If data is retrieved, the ‘map’ function is called and passed a reference to the extractData function. You don’t really need the extractData function, but I like having this function as it makes it easy for me to set a breakpoint in this function to see the data that has been returned. The extractData function is passed in the HTTP response object. Call the json function on this response object to retrieve the array of product data.

getProducts(): Observable<Product[]> {
return this.http.get(this.url).map(this.extractData).catch(this.handleErrors);
} private extractData(res: Response) { let body = res.json(); return body || {}; }

The last function in this class is handleErrors. 

private handleErrors(error: any): Observable<any> {
  let errors: string[] = [];
  switch (error.status) {
    case 404: // Not Found
      errors.push("No Product Data Is Available.");
      break;
    case 500: // Internal Error
      errors.push(error.json().exceptionMessage);
      break;
    default:
      errors.push("Status: " + error.status 
                  + " - Error Message: " 
                  + error.statusText);
      break;
  };
  console.error('An error occurred', errors);
  return Observable.throw(errors);
}

This function should be used to publish the error information. I am just going to output the error to the console window of the browser. You inform the calling code of the error by calling the throw function on the Observable class. The Observable returns an array of strings. For this sample, you only need a single error message to be returned, however, in other cases you will want multiple error messages. You will see examples of this when handling validation error messages.

Update the app.module.ts File

In order to inject the HTTP service in the ProductService class you need to import the HttpModule in the app.module.ts file. You then add this imported module to the @NgModule imports property. This makes it available for use in any of your classes. Angular will take care of injecting this service whenever it sees a reference to Http as you saw in the constructor of your ProductService class.

import { HttpModule } from '@angular/http';

@NgModule({
  imports: [BrowserModule, AppRoutingModule, HttpModule ]
  ...
})

Remember you marked your ProductService as an @Injectable class. This means you want Angular to automatically inject this service into any of your class constructors. To accomplish this, you also need to import the ProductService in your app.module.ts file and add this class to the providers property.

import { ProductService } from "./product/product.service";

@NgModule({
  imports: [BrowserModule, HttpModule ],
  declarations: [ AppComponent ],
  bootstrap: [AppComponent],
  providers: [ProductService]
})

Update Product List HTML Page

It is now finally time to build the components needed to display a list of products like the page shown in Figure 1. Open the \app\product\product-list.component.html you built earlier. Add the HTML shown in Listing 2.

<h2>Product List</h2>

<div class="row"
     *ngIf="products && products.length">
  <div class="col-xs-12">
    <div class="table-responsive">
      <table class="table table-bordered
                    table-condensed table-striped">
        <thead>
          <tr>
            <td>Product Name</td>
            <td>Introduction Date</td>
            <td>URL</td>
            <td class="text-right">Price</td>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let product of products">
            <td>
              {{product.productName}}
            </td>
            <td>
              {{product.introductionDate | date }}
            </td>
            <td>
              {{product.url}}
            </td>
            <td class="text-right">
              {{product.price | currency:'USD':true }}
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<div class="row"
     *ngIf="messages && messages.length">
  <div class="col-xs-12">
    <div class="alert alert-warning">
      <ul>
        <li *ngFor="let msg of messages">{{msg}}</li>
      </ul>
    </div>
  </div>
</div>
Listing 2: Build the HTML table using the Angular *ngFor directive.

In the HTML that builds the product table, use the *ngIf directive to test if there any products. If there are, then you build an HTML table using the *ngFor directive to iterate over a collection named products in the ProductListComponent class. Each time through the loop a product object is assigned to a local variable named product. Use this product variable to retrieve each property of the Product class and display the value of each in each cell of the table.

At the end of the HTML is another <div> element which displays any error messages returned from the ProductListComponent class. This message is set if an error occurs in the retrieval of the product data from the Web API.

Update Product List Component

From the HTML in Listing 2 you know that your ProductListComponent class (Listing 3) must expose two properties to the HTML page; products and message. This class must be injected with the ProductService class from which you can request the product data. This class must also have some exception handling in case the Web API throws an exception or does not return data. Open the product-list.component.ts under the \app\product folder. Modify this class to look like the code contained in Listing 3.

import { Component, OnInit } from "@angular/core";

import { Product } from "./product";
import { ProductService } from "./product.service";
@Component({
  moduleId: module.id,
  templateUrl: "./product-list.component.html"
})
export class ProductListComponent implements OnInit {
  constructor(private productService: ProductService) {
  }
  ngOnInit(){
    this.getProducts();
  }
  // Public properties
  products: Product[] = [];
  messages: string[] = [];
  private getProducts(){
    this.productService.getProducts()
      .subscribe(products => this.products = products,
       errors => this.handleErrors(errors));
  }
  private handleErrors(errors: any) {
    for (let msg of errors) {
      this.messages.push(msg);
    }
  }
}

Listing 3: The product listing component.

Let’s look at each piece of the ProductListComponent class and learn why you need each line of code, and what each does. The first step is to import the various classes you use within this class. The following four imports are used in this class.

import { Component, OnInit } from "@angular/core";
import { Product } from "./product";
import { ProductService } from "./product.service";

You can see the Component import is needed for the @Component decorator. The templateUrl property in the decorator points to the product-list.component.html page you just created.

The OnInit import is needed to support the ngOnInit lifecycle event which is raised when the Angular engine creates an instance of this class. We use the ngOnInit function to call the getProducts function to retrieve the product data from the Web API.

The last two imports are easy to understand; we need the Product class because we are expecting to fill an array of Product objects from our call to the Web API. The ProductService class is used to call that Web API.

The constructor for this class receives the ProductService class from Angular injection and assigns that instance to the private property named productService.

constructor(private productService: ProductService) {
}

Two properties are declared in this class; products and messages. The products property is an array of Product objects. Initialize this property to an empty array by using the = []; syntax after the declaration of this property. The messages property is a string array used to display any error messages on the page.

// Public properties
products: Product[] = [];
messages: string[] = [];

The getProducts function calls the getProducts function from the ProductService class. This function returns an observable array of products from the map function, so use the subscribe function to retrieve the product array and assign it to the products property in this class. If an error occurs when calling the Web API, the function in the second parameter to the subscribe function is called. Take the error object passed back from the ProductService and pass that to the handleErrors function in this class.

getProducts() {
  this.productService.getProducts()
    .subscribe(products => this.products = products,
     errors => this.handleErrors(errors));
}

The handleErrors function simply loops through the list of error messages sent by the Observable.throw() in the Product service and adds each message to the messages array. This array is bound to an unordered list on the product-list.component.html page.

handleErrors(errors: any) {
  for (let msg of errors) {
    this.messages.push(msg);
  }
}

Update ProductController

Now that you are retrieving data from the Web API you created, you no longer need the code in the Get() or the Post() methods in your ProductController class. Open the ProductController class and delete the Post() method. Modify the Get() method to look like the following.

public ActionResult Product() {
    return View();
}

Summary

In these last two blog posts you added Angular into an MVC application. You learned to use Angular routing to route from MVC to Angular. You created a Web API on your server to return data to an Angular service. You then displayed a list of the data returned on an HTML page. In the next blog post you learn to add a new product.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC – Part 2. NOTE: After downloading the sample, you will need to right mouse click on the package.json file and select the menu “Restore Packages”. Create the product by locating the \SqlScripts\Product.sql file and run the script in a SQL Server database. Open the Web.config file in the project and update the connection string to point to your server and database name.

Add Angular to MVC – Part 1

Many of us have MVC applications currently running. You would like to start using Angular 2 in your web applications, but don’t have the time to completely rewrite your MVC application. It would be nice if you could just re-write one or two pages in Angular and keep the rest of your MVC project in place. Turns out you can. In this blog post, you will learn how to add Angular to your MVC applications. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.

Setup Your Machine

Before you begin to use Angular, you must prepare your development machine. There are two tools required, as well as you must configure Visual Studio 2015 to use TypeScript. You should also get the Angular quick start project.

Install Node

If you have not done so already, download and install Node.js and NodeJS Package Manager (npm). You can get these two packages at https://nodejs.org/en/download/. Follow the instructions for downloading and installing these tools on nodejs.org.

Configure Visual Studio 2015

Most developers are using TypeScript for Angular development. Ensure you have downloaded and installed TypeScript 2.x for Visual Studio 2015. Select the Tools | Extensions and Updates menu to bring up the Extensions and Updates window (Figure 1) for Visual Studio. Click on the Installed node in the tree view and look through your list and see if you have installed TypeScript 2.x. If you have an older version of TypeScript you need to update it.


Figure 1: Use the Extensions and Updates window to check for TypeScript.

To update to TypeScript 2.x, click on the Online node in the tree view and perform a search for TypeScript. Locate the latest version of TypeScript and download and install it into Visual Studio.

One last configuration item for Visual Studio is to select the Tools | Options menu and expand the Projects and Solutions node. Click on the External Web Tools (Figure 2). Ensure that the $(PATH) variable is located above any $(DevEnvDir) variables if they exist in your environment. In my installation of Visual Studio, these variables did not exist.


Figure 2: Make sure the $(PATH) variables is high in your location of external tools.

Download Angular Quick Start

Now that you have configured Visual Studio, you are going to need some configuration files and some TypeScript files to make it easier to get started with Angular. The Angular team has created a sample project that contains these files. This sample project is not a Visual Studio project, so you are only going to use some of the files from this project and not bring all of them into your project. Download the quick start zip file from https://github.com/angular/quickstart. After the zip file has been downloaded, unzip the files into a folder on your hard drive.

Sample MVC Application

Let’s look at a sample MVC application, named PTC, that I am going to use as a demonstration (Figure 3). You can download this sample, and the final sample by following the instructions at the end of this blog post.


Figure 3: A sample MVC application to display a product list

This is a vanilla MVC application that does not have the Web API in it. It has the following classes that are of interest.

  • ProductController- The MVC controller to display the \Views\Product\Product.cshtml page.

  • Product - An entity class to represent a product object
  • PTCData-Extensions - Additional validation for Product data
  • PTCData - Entity Framework generated data layer.

  • PDSAPageModeEnum - Enumeration for the mode that page is currently in
  • ProductViewModel - A view model that is called from the ProductController to retrieve data

  • _ProductDetail.cshtml - A partial page to display, add, and edit a single product record.
  • _ProductList.cshtml - A partial page that displays a list of product data
  • Product.cshtml - A MVC page to list or edit product(s) retrieved from the view model.

Install Product Table

Before you can run this sample, you need to download the sample project, locate the \SqlScripts folder and install the Product.sql into a SQL Server database. After you have created the Product table, open the Web.config file and adjust the connection string to point to your server and your database.

Test the Page

You can now run the Product.cshtml page and ensure you get something that looks like Figure 4.


Figure 4: The product list page

Add Angular Files

You need to add a few Angular files from the quick start project you downloaded. Prior to doing this, add a new folder named \app to your MVC project. Using Windows Explorer, navigate to the folder where you extracted the quick start files. Do NOT copy all these files into your Visual Studio project, you do not need all of them. Locate and copy the following files into the root of your new Visual Studio project.

  • \package.json
  • \tslint.json
  • \src\tsconfig.json

Copy the file \src\systemjs.config.js into the \scripts folder of your VS project.

Copy the file \src\main.ts into the \app folder of your VS project. You will most likely receive a message that your project has been configured to support TypeScript. It will ask you if you want to search for TypeScript typings. Just answer No as you are not going to need these right now.

Expand the \src\app folder in the quick start folder and copy the following TypeScript files located in this folder into the \app folder you created in your VS project. 

  • app.component.ts
  • app.module.ts

Restore Packages

Even though you added some TypeScript files, nothing is going to work yet. You need to download a new folder using the NodeJS Package Manager (npm). Visual Studio can download all of these files using npm, but you must first close and reopen Visual Studio. If you don’t close and reopen Visual Studio, the appropriate menu item needed will not show up. Go ahead and close your instance of Visual Studio now, then, reopen the ProductApp project. Right mouse click on the package.json and select Restore Packages (Figure 5) from the menu. This process takes a little while, so be patient.


Figure 5: The Restore Packages menu only shows up after closing and reopening Visual Studio.

After the installation of the packages is complete, click on the Show All Files icon in the Solution Explorer window. You should see a new folder has appeared named node_modules (Figure 6). DO NOT include this folder in your project, you do not need all these files in your project. They are just there to support the various libraries you use when developing with Angular.


Figure 6: Don’t include the node_modules folder in your project.

Add Links to the MVC Shared Layout

Navigate to where you installed the quick start files and open the \src\index.html page. You need to copy a few lines of code from this page into the \Views\Shared\_Layout.cshtml file. Open the _Layout.cshtml file in Visual Studio. Locate the following lines of code in the index.html page and paste them into the <head> section of the _Layout.cshtml file.

<base href="/">

I like to the above line of code as the first one after the <head> element. Locate the next lines that start with the comment shown below and copy them all after the @Scripts.Render() and before the </head> element.

<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
  System.import('main.js')
       .catch(function(err){ console.error(err); });
</script>

Since the _Layout is in a different location in your project from the index.html in the quick start project, you need to fix up the src attributes. Add a forward slash in front of each of the above references for the node_modules.

<script src="/node_modules/core-js/client/shim.min.js"></script>
<script src="/node_modules/zone.js/dist/zone.js"></script>
<script src="/node_modules/systemjs/dist/system.src.js"></script>

For the systemjs.config.js file, you placed that into the Scripts folder, so you need to add /scripts/ in front of that file reference.

<script src="/scripts/systemjs.config.js"></script>

Locate the <script> tag that is responsible for importing the main.js file which is transpiled from the main.ts file. This code is shown in the following code snippet.

<script>
  System.import('main.js').catch(function(err){ console.error(err); });
</script>

Since you moved the main.ts file into the \app folder, modify the parameter passed to the import function to ‘app/main’ as shown in the following snippet.

<script>
  System.import('/app/main').catch(function(err){ console.error(err); });
</script>

In the quick start project, the main.ts file was expecting to find the app.module.ts file in the \app folder below the root folder. However, since these two files are now located in the same folder, you need to open the main.ts file located in the \app folder and locate the following line of code.

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

Remove the ‘/app’ portion from this line of code so that the final code looks like the following snippet.

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

You should run your Product page again just to make sure that your application still compiles and runs.

Eliminate Hard-Coded HTML Template

The app.component.ts file contains a template property to output some HTML. I do not like having HTML written in TypeScript, so create an HTML page to display the HTML for the landing page of your Angular application.

Right mouse click on the \app folder and select Add | HTML Page and set the name to app.component.html. Click the OK button. Remove the HTML in the page and replace it with the following:

<router-outlet></router-outlet>

In the HTML above you are adding a location for the Angular routing engine to place HTML that comes from different components. For example, you are going to create a product list component and corresponding HTML page to display product data. That HTML will be placed in between these two tags.

Open the \app\app.component.ts file and you will see the following declaration.

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>`,
})

Now that you have created the new HTML page, you are going to remove the hard-coded HTML from the template property in the @Component decorator. I have found that if you have any more than a few lines of HTML, this property becomes cumbersome to maintain. Let’s change the template property to the templateUrl property and set that property to the string ‘app.component.html’.

@Component({
  moduleId: module.id,
  selector: 'my-app',
  templateUrl: './app.component.html'
})

Another change you are making to this @Component decorator is adding the property moduleId and setting that property to the value module.id. This property helps Angular understand relative paths in relation to the current component. If you did not use the moduleId property, then you change the templateUrl property to ‘./app/app.component.html’. I prefer to use relative paths as opposed to having to fully quality the path in relation to the root.

The AppComponent class that came with the quick start sample has a name property set to the word ‘Angular’. Just delete this code and make your AppComponent class look like the following:

export class AppComponent { }

You will learn more about how the routing works a little later in this article. But first, let’s build the product list component and HTML.

Stub the Angular Product Components

To test that you can route to a Product list page from your MVC page, create a couple of new files.

Right mouse click on the \app folder and add a new folder called \product. Right mouse click on this product folder and add a new HTML page called product-list.component.html. Delete all the HTML in this new page and add the single line below:

<h2>Product List</h2>

Right mouse click on the product folder and add a new TypeScript file named product-list.component.ts. Add the following code:

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

@Component({
  moduleId: module.id,
  templateUrl: "./product-list.component.html"
})
export class ProductListComponent { }

Update the app.module.ts File

Add the ProductListComponent class to the app.module.ts. Open the app.module.ts file and add an import near the top of the file that looks like the following.

import { ProductListComponent } from "./product/product-list.component";

In the @NgModule decorator, add the ProductListComponent class to the declarations property by adding a comma after AppComponent and typing in the name of this class as shown below.

declarations: [ AppComponent, ProductListComponent ]

Angular Routing

No Angular application would be complete without routing. Routing allows us to navigate from one page to another. The app.component.html page you just created is the main page for getting to other pages in our application via the routing engine. Add a new TypeScript file called app-routing.module.ts and add the code shown in Listing 1.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from "./product/product-list.component";
const routes: Routes = [
{
    path: 'productList',
    component: ProductListComponent
  },
  {
    path: 'Product/Product',
    redirectTo: 'productList'
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Listing 1: The routine class defines one or more routes for your application.

The important part of the app-routing.module.ts file is the constant named routes. This constant contains an array of Route objects. Each object has a path property which is used in the routerLink attribute you created earlier. If the path matches the address in the browser, then the component property is used to instantiate an instance of the object listed in this property. Once this class is instantiated, the HTML defined in that classes templateUrl property is inserted into the location where the <router-outlet> directive is located.

The routes constant is added to the singleton instance of the Router service using the forRoot function. You can see this in the imports property of the @NgModule decorator. You can add as many routes to the routes constant as you need for your application.

Overview of Angular Routing

With all that description above, I thought a graphical representation (Figure 7) of the Angular routing you are using in this application might help clear things up a little.


Figure 7: An overview of the Angular routing process.

  1. Anytime the browser address bar is updated via an anchor tag, or other mechanism, the last part of the address is matched up to one of the routes added to the singleton instance of the Router service.
  2. Once it finds the path in the Router service, it instantiates the class listed in the component property.
  3. After the class is instantiated, the templateUrl property is read from the @Component decorator.
  4. The HTML from the file listed in the templateUrl property is loaded and inserted into the DOM at the location of the <router-outlet> directive.

Update the app.module.ts File

Just as you have done with all your other classes you created, you need to declare your intention to use routing in the app.module.ts file. Open the app.module.ts file and add the following import statement near the top of the file.

import { AppRoutingModule } from './app-routing.module';

Add the AppRoutingModule to the imports property in the @NgModule decorator. The imports property should now look like the following code snippet.

imports: [BrowserModule, AppRoutingModule ]

Call our Angular page from the Product.cshtml page

Open the \Product\Product.cshtml page and delete all the code. Write the following:

<my-app>Loading products...</my-app>

Remember the <my-app> is the selector in the app.component.ts file. When this selector is encountered, the Angular system kicks in and starts loading our Angular components to run. You have now successfully removed yourself from the MVC system and are now running all client-side!

Run the Page

At this point you should be able to run the page and see the <h1> you entered. Your page should like Figure 8.


Figure 8: You have rerouted to an angular page from an MVC page.

Summary

Congratulations! You have successfully integrated Angular into a Visual Studio MVC Application. There are a lot of steps to get Angular up and running, however, once you get the basics configured, adding new pages and new components is quite easy because you use the same pattern you learned in this post. In the next blog post, you add and call a Web API from an Angular service, and display the product data in an HTML page using Angular.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC - Part 1. To run the sample, locate the \SqlScripts\Product.sql file and run the script in a SQL Server database. Open the Web.config file in the project and update the connection string to point to your server and database name.

Caching for Non-Web Applications – Part 2

In my last blog post, entitled Caching for Non-Web Applications – Part 1 I introduced you to the MemoryCache class. This class allows you to create a cache for any type of application just like in ASP.NET applications. The great thing about the MemoryCache class is there is no reliance on the System.Web namespace. This means you are free to use the MemoryCache class in any type of application such as Windows Forms, WPF and Windows Services. The last blog post showed you how to add and retrieve data from the cache. This blog post expands on the last one and shows you additional methods you can take advantage of to work with cache data.

Add or Get Existing

The AddOrGetExisting method has the same signature as the Add method. However, instead of returning a true or false value, it will either return a null if the key/value pair was added to the cache, or returns the object if the key value is found already in the cache.

object ret;
ret = MemoryCache.Default.AddOrGetExisting("Key2", 
         "Value 2", DateTimeOffset.Now.AddSeconds(5));
if (ret == null) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

AddOrGetExisting Using CachePolicy

Just like you are able to add a new item to the cache using the CachePolicy using the Add method, you can do the same with the AddOrGetExisting method. The code below will add a new cache item with a sliding expiration of 5 seconds.

object ret;
CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 5);
ret = MemoryCache.Default.AddOrGetExisting("Key2", 
                   "Value 2", pol);
if (ret == null) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

Set Method

The Set method adds an entry into the cache if the specified key value does not exist. However, if the key value does exist, it will update the value for that key in the cache. I prefer to use this method over the Add or AddOrGetExisting as I typically just want to either add, or update, and not worry about whether I have created the value previously or not. This method does not return any value.

MemoryCache.Default.Set("Key3", "Value 3", 
     DateTimeOffset.Now.AddSeconds(5));

You can use a CacheItemPolicy to set a sliding expiration on the item to cache.

CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 5);
MemoryCache.Default.Set("Key3", "Value 3", pol);

Notify Before Removal from Cache

Just before an item is to be removed from the cache because it is expiring, you may create a method to respond to this event. Write the following method to accept a CacheEntryUpdateArguments object. Within this method body you can write whatever code you want. In the sample below I am just writing out the Key and the reason for the removal.

private void UpdateHandler(CacheEntryUpdateArguments e) {
Console.WriteLine(e.Key);
Console.WriteLine(e.RemovedReason.ToString());
}

Once you have this method written, you assign that method to the UpdateCallback property of a CacheItemPolicy object. The code snippet below shows you how you assign this method to the UpdateCallback property.

CacheItemPolicy pol = new CacheItemPolicy();
pol.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3);
pol.UpdateCallback = UpdateHandler;
MemoryCache.Default.Set(THE_KEY, "Update 3", pol);

If you run the above code and wait a few seconds, you will see a couple of messages printed in the Output window.

Within the UpdateHandler method you also have the option to change the value in the cache and place it back into the cache. You do this by assigning a new CacheItem object with the same key and a value to the e.UpdatedCacheItem property. You must also set the e.UpdatedCacheItemPolicy property to a new CacheItemPolicy object. When this method ends, the item is not removed and is updated within the cache. The code to do this is shown below.

private void UpdateHandler(CacheEntryUpdateArguments e) {
Console.WriteLine(e.Key);
Console.WriteLine(e.RemovedReason.ToString());
e.UpdatedCacheItem = new CacheItem(e.Key, "My New Value");
CacheItemPolicy pol = new CacheItemPolicy();
pol.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10);
pol.UpdateCallback = UpdateHandler;
e.UpdatedCacheItemPolicy = pol;
}

Notify After Removal from Cache

If you do not care to update an item in the cache, you may just respond to the RemovedCallback event. The design pattern is similar to what you just wrote in the previous example. First create a method that accepts a CacheEntryRemovedArguments object. The only two properties in that object you care about are the e.RemovedReason and the e.CacheItem. The e.CacheItem object contains the item that was just removed from the cache.

private void RemovedHandler(CacheEntryRemovedArguments e) {
  Console.WriteLine(e.RemovedReason.ToString());
  Console.WriteLine("Removed Handler: " + e.CacheItem.Key);
}

With the RemovedHandler method created, you may now set the RemovedCallback property of a CacheItemPolicy object. You then use that CacheItemPolicy object to add a new item into the MemoryCache object.

CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 3);
pol.RemovedCallback = RemovedHandler;
MemoryCache.Default.Set("Key3", "Remove 3", pol);

If you run the above code, then either let the value expire, or explicitly remove the item from cache, the RemovedHandler method is called.

Summary

In this blog post you learned to call the AddOrGetExisting method. This method is different from the Add method as it either adds an item to the cache, or retrieves one if they key already exists. You also learned to use the Set method to either add a new item to the cache, or update an item in the cache if it already exists. Providing a couple of callback functions to the CacheItemPolicy lets you respond when an item is about to be deleted from the cache, or after it has already been deleted.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Caching for Non-Web Applications.

Caching for Non-Web Applications – Part 1

A great feature of ASP.NET applications is the Cache class which allows you to store values that are commonly used. Caching data can avoid round-trips to database servers, which can save a lot of time. Until 2010, there was no good way in a Windows Service, Windows Form or WPF application to cache data except by writing your own class. Enter the MemoryCache class, part of the System.Runtime.Caching namespace. This class allows you to add data to a cache and set a time-out so that data can be removed from memory when it is no longer used. This blog post will show you the basics of using this class.

Add Key/Value Pairs

The MemoryCache class has a Default property that refers to a single instance of a MemoryCache. For most applications, you only require one cache object. Of course, you may always create a new instance of a MemoryCache object if you need additional cache objects for your application. In this blog post you are going to just use the default instance.

To add a new value to the cache, you supply three items; a unique key, a value to insert, and how long you want the value to stay in the cache. You are not allowed to insert a null value into the cache, but any other value is allowed. The following code snippet shows the basics of adding a new value to the default MemoryCache instance.

MemoryCache.Default.Add("Key1", "Value 1",
   DateTimeOffset.Now.AddSeconds(5));

The first parameter you pass to the Add method is a unique key value that you use to retrieve the value later. The second parameter is the value to add. In this case, I just added a simple string, however, this can be any data type. The last parameter is a DateTimeOffset type to specify how much time the item should stay in the cache before it is automatically removed.

Check to See if Value was Inserted

Instead of just calling the Add method blindly as I did in the previous code snippet, you should check to see if the value was added. If you attempt to add the same key value to the same cache, the call will fail and the value is not updated. You can check the return value from the Add method to determine if the value was added.

bool ret;
ret = MemoryCache.Default.Add("Key1", "Value 1", 
           DateTimeOffset.Now.AddSeconds(5));
if (ret) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

Add Using a Cache Policy

If you wish to set other meta-data information about the item in your cache, create a CacheItemPolicy object and use that to add your item. One of the things you can do with a CacheItemPolicy is set a SlidingExpiration property. The previous code set an absolute time to expire your cache object. The SlidingExpiration property is a TimeSpan to keep the item in your cache, but that time span renews each time the item in the cache is accessed.

bool ret;
CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 5);
ret = MemoryCache.Default.Add("Key1", "Value 1", pol);
if (ret) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

In the previous code the SlidingExpiration property of a new CacheItemPolicy object is set to 5 seconds. If you access the item in the cache at 4 seconds, then the item will stay in the cache for another 5 seconds. If you access it again after 3 seconds, then another 5 seconds is added on. However, if you do not access the item, then that item is removed from the cache after 5 seconds has elapsed.

Add Using the Default Indexer

If you wish to add an item without setting any expiration time, you can use the default indexer. Pass the key name within square brackets to either add a new, or update an existing key, within the MemoryCache object.

MemoryCache.Default["Key1"] = "Value 1a";

Access the Data in the Cache

After you have entered data in the cache, you need to retrieve it, check to see if it is still in there, count it, etc. The MemoryCache object, of course, supplies you with the appropriate methods to perform these operations. To retrieve a value, use the Get method. You should check to see that the key exists before you retrieve it.

if (MemoryCache.Default.Get("Key1") != null) {
MessageBox.Show(
    MemoryCache.Default.Get("Key1").ToString());
}
else {
MessageBox.Show("Cache Item Does Not Exist");
}

If you attempt to Get a key that does not exist within the cache a null value is returned. If it does exist, the Get method returns an object type to you. You will need to cast the data to the appropriate data type prior to using it.

Use the Default Indexer

Instead of using the Get method you may also use the indexer property to retrieve a value from the cache. The indexer to the MemoryCache object is just like any other .NET indexer. You pass the key name within square brackets to the MemoryCache object and it will return the value if one exists. The code below provides the exact same functionality as the previous sample.

if (MemoryCache.Default[THE_KEY] != null) {
MessageBox.Show(MemoryCache.Default[THE_KEY].ToString());
}
else {
MessageBox.Show("Cache Item Does Not Exist");
}

Using the Contains Method

Instead of using the Get method and having it return a null if the key does not exist, use the Contains method to just check to see if a key exists in the cache. The code below shows an example of using the Contains method.

if (MemoryCache.Default.Contains("Key1")) {
MessageBox.Show("Cache Item Exists");
}
else {
MessageBox.Show("Cache Item Does NOT Exist");
}

Count the Items in the Cache

Call the GetCount method to determine how many items are currently cached in the MemoryCache object. This method returns an integer value.

MessageBox.Show(MemoryCache.Default.GetCount().ToString());

Remove an Item from the Cache

At some point, you might with to manually remove an item from the cache, instead of just letting the item expire. To accomplish this, call the Remove method passing in the key value.

MemoryCache.Default.Remove("Key1");

Summary

In this blog post you were introduced to the MemoryCache object. This object allows you to cache data in any type of application, even a non-web application. This class is like the ASP.NET Cache object in that it allows you to time-out values you put into the cache. You can add values to the cache using the Add or default indexer. Retrieve values from the cache using the Get method or the default indexer. You may also count, check for key existence, and remove items from the cache. In the next blog post, you will learn to use the AddOrGetExisting and Set methods. In addition you will learn how to be notified when an item is removed from the cache.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Caching for Non-Web Applications.

Unit Testing Using the Command Line

This is another in my series of blog posts on unit testing. If you are not familiar with unit testing, go back and read these posts.

In this post, you are going to learn to run unit tests from the command line. This allows you to schedule tests using task manager or any other automated scheduling tool. The VSTest.Console.exe is the tool you use as a .NET developer to run your unit tests in your .NET test dll.

Simple VSTest.Console Run

The VSTest.Console.exe is typically installed into the following path: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow. When you run this tool, you need to prefix this path to the .exe, or add this path to the PATH environment variable. If you open a Developer Command Prompt for VS2015 (Figure 1) this path will already be included.


Figure 1: Use the Developer Command Prompt

If you have been creating the project as you have been following along with this series of blog posts, you have a DLL named MyClassesTest.dll located in the \bin\Debug folder of where your project is located on your disk. Open a Developer Command Prompt and navigate to that folder. Type in the following in your command window.

VSTest.Console.exe MyClassesTest.dll

Press the Enter key and you should see something that looks like Figure 2. You may or may not have the warnings depending on if you added the DeploymentItem attributes.


Figure 2: A simple run of the VSTest.Console

View Installed Loggers

Now that you know how to run your unit tests from the command line, you now need to learn to log the results to a file that you can look at later. If you are going to be running your unit tests overnight, you want to come back in the morning to see the results. If they are just sitting in a command window, you could accidentally close that window and you would lose the results. Instead, the VSTest.Console utility has a set of loggers that you can use. Type in the following in the command window.

VSTest.Console.exe /ListLoggers

Press the Enter key to see a screen that looks like Figure 3.


Figure 3: View the log options you can use with VSTest.Console

The ConsoleLogger is the one you are currently looking at in your command window. The TfsLogger is useful if you are using Team Foundation Server as it allows you to send the results to TFS so you can assign work items based on any failed unit tests. The last one is the one that will be useful if you do not have TFS. The TrxLogger creates a .trx file which you can load into Visual Studio and see a list of all of your unit tests. You can then click on each test and see the results, and the outputs for that test.

Using Logger

Let's take a look at using the TrxLogger option when using the VSTestConsole.exe utility. Type in the following into the command window.

VSTest.Console.exe MyClassesTest.dll /Logger:trx

Press the Enter key and the unit testing will run. The results are now stored in a .trx file located in the TestResults folder under your \bin\Debug folder. Each time you run a new .trx file is added with a later date and time add to the file name.

Double click on any of the *.trx files and it will load the results into a Test Results window in Visual Studio as shown in Figure 4. You can double-click on any of the tests to see the output from that test. 


Figure 4: Visual Studio can display all results from a .trx file

Run Specific Test(s)

The VSTest.Console utility allows you to specify single or multiple test method names to run. If you are just testing one or two items, there is no reason to run all the tests in your DLL. Add the /Tests parameter on the command line followed by a comma-delimited list of method names you wish to execute. Type the following into your command window.

VSTest.Console.exe MyClassesTest.dll /Tests:FileNameDoesExist

Press the Enter key and you should see a result that looks like Figure 5.


Figure 5: The /Tests parameter does pattern matching on your method names

Notice that multiple tests where run even though you only specified a single name. This is because the /Tests parameter uses pattern matching on your method names. It will find any method that starts with the name you pass in and run those methods.

You can use a comma-delimited list to specify different sets of methods to run. Type the following into your command window.

VSTest.Console.exe MyClassesTest.dll 
   /Tests:FileNameDoesExist,
    FileNameNullOrEmpty_ThrowsArgumentNullException

Press the Enter key and you should see results that look similar to Figure 6.


Figure 6: You may use a comma-delimited list after the /Tests parameter

Filter Tests to Run based on Attributes

As mentioned in the blog post on attributes, the Priority attribute is not used by the unit test framework. However, when you use the VSTest.Console utility, you are allowed to filter based on the Priority attribute. Type in the following to the command window and you can run just those methods that have the Priority(1) attribute.

VSTest.Console.exe MyClassesTest.dll 
   /TestCaseFilter:"Priority=1" 

The /TestCaseFilter lets you specify attributes and specific names of methods to run. For example, if you want to just run one test with the name of FileNameDoesExist, you type in the following into the command window to run that one test.

VSTest.Console.exe MyClassesTest.dll 
   /Name:"FileNameDoesExist" 

Another attribute you can specify to run is TestCategory. Run the following in the command window to just run those tests marked with [TestCategory(“NoException”)].

VSTest.Console.exe MyClassesTest.dll
   /TestCaseFilter:"TestCategory=NoException"

You are not allowed to use both the /TestCaseFilter parameter and the /Tests parameter together. You must just run one or the other.

Summary

Running unit tests in a batch is made easy with the VSTest.Console.exe utility. This utility allows you to log the output to a file for later review. You can also log to TFS to help you assign work items to unit tests that fail. The VSTest.Console utility also lets you filter the tests to run using either a /Tests parameter, or a /TestCaseFilter parameter.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Unit Testing Using the Command Line.

Using Assert Classes and Methods in Unit Tests

If you have been following my blog posts on unit testing, you have used the Assert class to signify if a unit test is successful or not. The following are my previous posts on unit testing. If you are not familiar with unit testing, go back and read these posts.

You have used the Inconclusive, IsTrue, IsFalse and Fail methods. In this blog post you will learn about some of the other methods you can utilize in the Assert class. In addition you will learn about two additional assert classes you may take advantage of when writing unit tests.

Assert Class

There are many methods in the Assert class. I won’t explain each one, but I will expose you to some so you can get an idea of what is available to use. You should search the MSDN documentation to see the complete list of properties and methods available to you in the Assert class.

Common Parameters to Assert Methods

Most of the methods you may invoke on the Assert class include an overload that allows you to specify a message to display in the test results. An additional overload lets you specify the message using the standard string.Format() tokens and a parameter array of the values to use to replace into the message.

[TestMethod]
public void FileNameDoesExistSimpleMessage() {
FileProcess fp = new FileProcess();
bool fromCall;
fromCall = fp.FileExists(_GoodFileName);
Assert.IsTrue(fromCall, "File Does Not Exist.");
}

When you run the above test, your Test Explorer window will show the hard-coded message after you click on the failed test (Figure 1).


Figure 1: Display hard-coded messages into the results window.

To include some of the data you gathered during the test, use the format items just as you use in the string.Format() method.

[TestMethod]
public void FileNameDoesExistSimpleMessageWithParams() {
FileProcess fp = new FileProcess();
bool fromCall;
fromCall = fp.FileExists(_GoodFileName);
Assert.IsTrue(fromCall, 
"File {0} Does Not Exist.", 
_GoodFileName);
}

When you run this test, you get the message shown in Figure 2.


Figure 2: Display messages with data from the unit test itself.

AreEqual Method

The AreEqual method compares two variables of the same data type to determine if they are equivalent. There are overloads for each of the different data types such as double, single, int, etc. You may add your own custom message, and specify format items as explained earlier. Below are just a few of the data types you can compare.

Assert.AreEqual(int, int);
Assert.AreEqual(bool, bool);
Assert.AreEqual(double, double);

Here is an example using integer data types.

[TestMethod]
public void AreEqualTest() {
int x = 1;
int y = 1;
Assert.AreEqual(x, y);
}

When the two variables you wish to compare are of a string data type, you may also specify a CultureInfo object to handle string comparisons based on the language of the user. You may also specify a boolean value to perform a case-sensitive or case-insensitive comparison of the strings.

AreEqual(string, string)               // case-insensitive
AreEqual(string, string, true)         // case-sensitive
AreEqual(string, string, CultureInfo)  // Use a culture

AreNotEqual Method

To compare two values to see if they are not equal you use the AreNotEqual method. This method, like the AreEqual method, has several overloads you can use based on the different data types. Below is a sample of using the AreNotEqual method.

[TestMethod]
public void AreNotEqualTest() {
int x = 1;
int y = 2;
Assert.AreNotEqual(x, y);
}

AreSame Method

To compare two objects to see if they are the same object. For example, if you write the following test:

[TestMethod]
public void AreSameTest() {
FileProcess x = new FileProcess();
FileProcess y = new FileProcess();
Assert.AreSame(x, y);
}

This test will fail, because the two objects point to two different objects. If you change this test to look like the following, where you assign the variable x to the variable y, then this test will succeed.

[TestMethod]
public void AreSameTest() {
FileProcess x = new FileProcess();
FileProcess y = x;
Assert.AreSame(x, y);
}

AreNotSame Method

To compare two objects to see if they are NOT the same object. In this case, the following test would succeed.

[TestMethod]
public void AreNotSameTest() {
FileProcess x = new FileProcess();
FileProcess y = new FileProcess();
Assert.AreNotSame(x, y);
}

IsInstanceOfType Method

You can use this method to determine if an object returned from a method is of a certain type. For example, you may have a method that returns an interface or a base class. When you call this method from your unit test, you might wish to compare the type of instance that is returned against the type you are expecting.

Look at the class diagram in Figure 3 in which you have a Person class with two properties. Both the Employee and the Supervisor classes inherit from the Person class. The PersonManager class has a method named CreatePerson that returns a Person object. Depending on the parameters you pass to the CreatePerson method, determines the type of object passed back. Either an Employee or a Supervisor object is returned.


Figure 3: A class diagram for our example

You can write a unit test to determine what the type is. In the unit test below, you pass a true value to the CreatePerson method. This value tells CreatePerson to return a Supervisor object. You use the IsInstanceOfType method to compare the variable per against the typeof(Supervisor).

[TestMethod]
public void IsInstanceOfTypeTest() {
PersonManager mgr = new PersonManager();
Person per;
per = mgr.CreatePerson("Paul", "Sheriff", true);
Assert.IsInstanceOfType(per, typeof(Supervisor));
}

IsNotInstanceOfType Method

This method is the same as the IsInstanceOfType, but checks to see if the instance returned is not of a specific type.

IsNull Method

Call this method to compare the return result from a method against null. If the return result is a null, then the test succeeds. In the unit test below, if you pass an empty string as the first name to the CreatePerson method, that method returns a null object.

[TestMethod]
public void IsNullTest() {
PersonManager mgr = new PersonManager();
Person per;
per = mgr.CreatePerson("", "Sheriff", true);
Assert.IsNull(per);
}

IsNotNull Method

This method is the same as the IsNull method, but checks to see if the value returned is not null.

StringAssert

When working with strings, you commonly need to check to see if one string is contained within another, or if one string matches a regular expression, or a string starts or ends with a specific character or other string value. To test these in a unit test, use the StringAssert class with any of the following methods.

  • Contains
  • DoesNotContain
  • Matches
  • DoesNotMatch
  • StartsWith
  • EndsWith

CollectionAssert

Many methods you write in your applications deal with collections of data. This data could be retrieved from a database, an XML file, or simply arrays of objects you create in your code. The CollectAssert class allows you to test a known set of data in a collection against the collection returned from the method you are testing. You do not need to do any looping through the collections, the CollectionAssert class will do all of that for you. Below is a list of the various methods you can use.

  • AllItemsAreInstancesOfType
  • AllItemsAreNotNull
  • AllItemsAreUnique
  • AreEqual
  • AreNotEqual
  • AreEquivalent
  • AreNotEquivalent
  • Contains
  • DoesNotContain
  • IsSubsetOf
  • IsNotSubsetOf

Summary

In this post, you learned more about the different methods in the Assert class. You also learned about two additional classes to help you test strings and collections. All methods in the various Assert classes contain overloads to allow you to add your own custom message to display in the test results. With this many methods available to you, writing your unit tests should go quickly.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Using Assert Classes and Methods in Unit Tests.

Add Attributes to Unit Tests

In my previous blog posts, I introduced you to creating unit tests with Visual Studio. The following is the list of blog posts published thus far.

You have seen a few different attributes such as [TestClass], [TestMethod], and [TestInitialize] used to decorate classes and methods. There are several more attributes that you should be aware of. You may or may not use all the attributes presented in this blog post, but you may have a need for them at some time or another.

DataSource Attribute

The unit test framework in Visual Studio can data-drive your unit tests. This means you can read data from a data store and execute a single test repeatedly using the data read in. A later blog post will discuss how to use data-driven tests, so this attribute is not covered here.

Description Attribute

Add a Description attribute to any unit test method to describe why you wrote a particular unit test. It is recommended that you use good, long, description name for the unit test method name as well as the Description attribute. This attribute is not used by the unit test framework, nor does it show up anywhere within the Test Explorer window.

[Description("Check to see if a file exists.")]
public void FileNameDoesExist() {
}
[Description("Check to see if file does not exist.")]
public void FileNameDoesNotExist() {
}
[Description("Check for a thrown ArgumentNullException.")]
public void 
  FileNameNullOrEmpty_ThrowsArgumentNullException () {
}
[Description("Check for a thrown ArgumentNullException
              using ExpectedException.")]
public void FileNameNullOrEmpty_
   ThrowsArgumentNullException_UsingAttribute () {
}

DeploymentItem Attribute

You may add as many DeploymentItem attributes as you need to specify files and folders to copy to the directory where the unit test runs. The DeploymentItem attribute accepts one or two parameters. The first parameter is a folder or a file name. The path is always relative the build output folder. The second parameter, if passed, is to a new path to copy the data in the first parameter. This second path can be relative to the output folder, or can be an absolute path. It is highly recommended you use a relative folder to allow tests to run seamlessly on different machines.

For our simple example, you do not need to use any DeploymentItem attributes, but below is a sample that would copy two files named DeploymentFile1.txt and DeploymentFile2.txt to the build output folder. It is assumed these two files already exist. If the folder or file does not exist, no error is thrown when running in Visual Studio, the test simply continues. However, if you use the command line utility, a warning is generated.

[DeploymentItem("DeploymentFile1.txt")]
[DeploymentItem("DeploymentFile2.txt")]
public void FileNameDoesExist() {
}

Ignore Attribute

The Ignore attribute is intended to be a temporary attribute you add to skip one or more unit test methods.

[Ignore]
public void FileNameDoesExist() {
}

Owner Attribute

The Owner attribute allows you to specify the name of the developer responsible for the unit test. This helps with the assignment of work items to the developer of a unit test that breaks.

[Owner("PaulS")]	
public void FileNameDoesExist() {
}
[Owner("PaulS")]
public void FileNameDoesNotExist() {
}
[Owner("JohnK")]
public void 
  FileNameNullOrEmpty_ThrowsArgumentNullException () {
}
[Owner("JohnK")]
public void FileNameNullOrEmpty_
   ThrowsArgumentNullException_UsingAttribute () {
}

After the test runs, right mouse click on the test results in the Text Explorer window and select the Group By menu (Figure 1).


Figure 1: Right mouse click to group by traits 

Next select the Traits menu to sort by any attributes you have added (Figure 2).


Figure 2: The Test Explorer window can group results by different attributes you add.

Priority Attribute

The Priority attribute, like the Owner attribute, is considered a “Trait” by the Test Explorer window. It is not used by the unit test framework itself, but can be grouped in the Test Explorer window (Figure 3). When using the VSTest.Console.exe command-line utility, you may filter the tests to run by the Priority attribute.

[Priority(0)]	
public void FileNameDoesExist() {
}
[Priority(1)]
public void FileNameDoesNotExist() {
}
[Priority(1)]
public void 
  FileNameNullOrEmpty_ThrowsArgumentNullException () {
}
[Priority(0)]
public void FileNameNullOrEmpty_
   ThrowsArgumentNullException_UsingAttribute () {
}


Figure 3: Tests can display under multiple traits.

TestCategory Attribute

The TestCategory attribute, like Owner and Priority, is a “Trait” you may group upon in the Test Explorer window (Figure 4). You define any category name you wish and assign that name to one or many tests. After all tests have run, you may filter and sort within the Test Explorer window on the category names.

[TestCategory("NoException")]
public void FileNameDoesExist() {
}
[TestCategory("NoException")]
public void FileNameDoesNotExist() {
}
[TestCategory("Exception")]
public void 
  FileNameNullOrEmpty_ThrowsArgumentNullException () {
}
[TestCategory("Exception")]
public void FileNameNullOrEmpty_
   ThrowsArgumentNullException_UsingAttribute () {
}



Figure 4: Categories are common traits to use besides Owner.

Timeout Attribute

Use the Timeout attribute to specify how long a specific method is allowed to run before the unit test framework kills the test and marks it as a failure. The value you specify is expressed in milliseconds. In the following example, the unit test framework will allow the method to execute for only 5 seconds before it will stop the execution and throw an AssertFailedException.

[Timout(5000)]
public void FileNameNullOrEmpty_
   ThrowsArgumentNullException_UsingAttribute () {
}

Summary

In this blog post you learned about many of the attributes you may apply to unit test classes and methods. The most common attributes you should add are Description, Owner and TestCategory. Description is optional, and should be used in combination with a descriptive unit test method name. The Owner, Priority and TestCategory attributes may be grouped within the Test Explorer window.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Attributes to Unit Tests.

String Interpolation in C# 6.0

I just found this handy shorthand for string.Format() which is available in C# 6.0. I am sure you have all used string.Format() before. Let's say you want to do a little formatting with some data in some variables like the following:

string first = "Paul";
string last = "Sheriff";
Console.WriteLine(string.Format("{0} {1}", first, last));

In C# 6.0 you may now use the string interpolation character, the dollar sign, instead of the string.Format().

string first = "Paul";
string last = "Sheriff";
Console.WriteLine($"{first} {last}");

Both of the above pieces of code will place the string "Paul Sheriff" into your output window.

I really like this new format. It is much easier to read and understand where exactly your variables are going without having to count the number of parameters and figure out which ones go where in your string.

You may also combine the $ with the @ sign if you are going to have a backslash in your string. You must specify them in the order $@ for the interpolation to work correctly. Here is an example of using these two together.

string dir = "Windows";
Console.WriteLine($@"C:\{dir}");

The above code will display the string "C:\Windows" in your output window.

Have fun with this new feature of C# 6.0.

Unit Test Initialization and Cleanup

This blog post continues our look at unit testing techniques. See my previous two blog posts for the sample used for testing.

In this blog post you learn about initialization and cleanup of the test environment. There are different methods of initialization and cleanup available to developers in Visual Studio. This blog post will introduce you to each and describe how to use each one.

Overview

There are six attributes you can use to perform initialization and cleanup. Which ones you use, depends on when you need to initialize something and/or clean something up. You can initialize/cleanup once for all classes within a unit test assembly. You can initialize/cleanup once for all test methods within a test class. You can initialize/cleanup before and after each test method runs within a class.

  • AssemblyInitialize
  • AssemblyCleanup
  • ClassInitialize
  • ClassCleanup
  • TestInitialize
  • TestCleanup

Add one or more of these attributes to a single method within your classes. You can see an example of how these attributes might be applied to a set of classes in your assembly displayed in Figure 1. The order in which these methods run is as follows.

  1. AssemblyInitialize
  2. ClassInitialize
  3. TestInitialize
  4. TestMethod, TestMethod, TestMethod, etc.
  5. TestCleanup
  6. ClassCleanup
  7. AssemblyCleanup


Figure 1: Overview of how test initialization and cleanup methods execute

Assembly Initialization and Clean Up

If you have several classes within a specific assembly and you need to create a database with some test records in some tables prior to running all, or the majority, of tests within these classes, create that database within a method decorated with the [AssemblyInitialize] attribute.

I think it is a good idea to create a separate class that is only used for assembly initialization and cleanup. Create a new class with the name of MyClassesTestInitialization within your test project. Add the following method to this class.

[AssemblyInitialize()]
public static void AssemblyInitialize(TestContext tc) {
  // TODO: Initialize for all tests within an assembly
  tc.WriteLine(“In AssemblyInitialize”);
}

Only one method within your entire assembly may be decorated with the [AssemblyInitialize] attribute. This method must be static and the test framework will pass in an instance of the TestContext object.

If you wish to delete any files or a database after all methods have run within an assembly, place the code to perform these operations within a method decorated with the [AssemblyCleanup] attribute. Only one method within your entire assembly may be decorated with the [AssemblyCleanup] attribute. This method must be declared as static.

[AssemblyCleanup()]
public static void AssemblyCleanup() {
  // TODO: Clean up after all tests in an assembly
}

Class Initialization and Clean Up

Just as you create classes in your application to encapsulate a specific set of functionality, organize your unit test classes the same way. In the test program for this series of blog posts, I have a FileProcess class. There is currently only one method in that class, but in a real application, there would probably be many methods all dealing with file IO.

In the FileProcessTest class you only have unit test methods that perform testing on methods within the FileProcess class. Within the unit test class, you may need to create some files (or a database, or other data) that is needed when running all or the majority of the test methods. If that is the case, create a method within that class that is decorated with the [ClassInitialize] attribute. Only one method within your test class may be decorated with the [ClassInitialize] attribute.

[ClassInitialize()]
public static void ClassInitialize(TestContext tc) {
  // TODO: Initialize for all tests within a class
  tc.WriteLine(“In ClassInitialize”);
}

If you create a file (or a database, or other data), you might want to delete that file, or other data after all of the tests have run in that class. Add a method to perform this cleanup within a method decorated with the [ClassCleanup] attribute. Only one method within your class may be decorated with the [ClassCleanup] attribute.

[ClassCleanup()]
public static void ClassCleanup() {
  // TODO: Clean up after all tests within this class
}

Test Initialization and Clean Up

The method decorated with the [ClassInitialize] attribute only runs once the first time a class is created. A method decorated with the [TestInitialize] attribute will run before each test method within that class. If you have four methods in a test class, the TestInitialize method will run four times. As an example, if you need a specific file name to exist before all, or the majority, of tests within a class are run, create a method like the one shown below.

[TestInitialize()]
public void TestInitialize() {
  // Create the Test.txt file.
  File.AppendAllText("Test.txt", "Some Text");
}

The above code will run before each test method is executed. If you did not clean up after each test, then you would have four lines of text within the Test.txt file. To clean up after each method, write a method that has the [TestCleanup] attribute attached to it.

[TestCleanup()]
public void TestCleanup() {
  // Delete Text.txt file
  if (File.Exists("Test.txt")) {
    File.Delete("Test.txt");
  }
}

Since this method runs after each unit test method, each new test method that runs will have a file available to it that only has a single line of text within it.

Check for Test Name

Within the TestInitialize method you can check the TestName property of the TestContext object to see if a specific test is being run. If it is, you could do a specific initialization just for that test. In the following sample, you check to see if the current test being run is “FileNameDoesExist” and if it does, you create the file contained within the _GoodFileName property. Of course, you are already doing this in the FileNameDoesExist method already, so you do not need to do it in the TestInitialize method, but it does show an example of what you could do.

[TestInitialize]
public void TestInitialize() {
TestContext.WriteLine("In TestInitialize");
if (TestContext.TestName == "FileNameDoesExist") {
if (!string.IsNullOrEmpty(_GoodFileName)) {
TestContext.WriteLine("Creating file: " + _GoodFileName);
// Create the 'Good' file.
File.AppendAllText(_GoodFileName, "Some Text");
}
}
}

Summary

In this blog post you learned about initialization and clean up for unit tests. You have three ways to perform initialization and clean up in the unit test framework. You just need to decide where in the process you need to perform the initialization. Then decide how much clean up you need, and write the appropriate methods, and decorate with the appropriate attributes.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Unit Test Initialization and Cleanup.


Avoid Hard-Coding in Unit Tests

In my previous blog post entitled Introduction to Unit Testing with Visual Studio, I introduced you to creating unit tests with Visual Studio. A method named FileExists was created to which you pass a file name to see if it exists. In the tests you created, you use hard-coded file names to test. Just as you wouldn’t hard-code values in a normal application, you should not do this with unit tests either. In this blog post you will learn to use constants, a configuration file, and how to create and delete test files.

Please go read the previous blog post and create the project, or download the project at http://www.pdsa.com/downloads and select “Introduction to Unit Testing” from the list.

Use a Constant

Constants are a great way to centralize hard-coded data that would otherwise be repeated throughout an application. In this case, you are going to replace the hard-coded file name used in the FileNameDoesNotExist method with a constant. At the top of the FileProcessTest class, add the following constant.

private const string BAD_FILE_NAME = @"C:\NotExists.bad";

Modify the FileNameDoesNotExist to use this new constant as shown in the code snippet below.

public void FileNameDoesNotExist() {
  FileProcess fp = new FileProcess();
  bool fromCall;
  fromCall = fp.FileExists(BAD_FILE_NAME);
  Assert.IsFalse(fromCall);
}

Use a Configuration File

A constant is a good option for the “bad” file name. For the “good” file name you wish to test to see exists, let’s add that to a configuration file so it can be modified easily. In fact, let’s add a replaceable token called [AppPath] that will figure out the appropriate path to use based on the machine the test is running upon.

Right mouse click on the FileProcessTest project and select Add | New Item… from the menu. From the template dialog select General | Application Configuration File. The name should already be set to App.config, so click on the Add button.

Within the <configuration> element add an <appSettings> section. Within the <appSettings> section add a key called GoodFileName with a value of [AppPath]\TestFile.text as shown below.

<appSettings>
  <add key="GoodFileName" value="[AppPath]\TestFile.txt"/>
</appSettings>

In order to retrieve this value from the configuration file, you need to use the ConfigurationManager class from the System.Configuration namespace. By default, the System.Configuration DLL is not added to a test project. Right mouse click on References folder in your FileProcessTest project and select Add Reference from the menu. From the dialog select Assemblies | Framework. Locate the System.Configuration dll and select the check box. Click the OK button to add this DLL to your test project.

At the top of the FileProcessTest class, add a using statement for the System.Configuration namespace.

using System.Configuration;

Add private field to FileProcessTest class to hold the value you are going to retrieve from the configuration file. Set the name of this field to _GoodFileName as shown below.

private string _GoodFileName;

Add a constructor to the FileProcessTest class in which you will read the GoodFileName value from the configuration file. Within this constructor you will replace the token [AppPath] with the value from the Environment.SpecialFolder.ApplicationData. This enumeration, supplied by .NET, when passed to the GetFolderPath, returns the pre-defined path for any data you need to store for this application. The location may vary from OS to OS, but on Windows 10 it is C:\\Users\\YOUR_USERNAME\\AppData\\Roaming. Write the constructor as shown below.

public FileProcessTest() {
  _GoodFileName =
     ConfigurationManager.AppSettings["GoodFileName"];
  if (_GoodFileName.Contains("[AppPath]")) {
     _GoodFileName = _GoodFileName.Replace("[AppPath]", 
        Environment.GetFolderPath(
          Environment.SpecialFolder.ApplicationData));
  }
}

Locate and modify the FileNameDoesExist method to use this new property name.

[TestMethod]
public void FileNameDoesExist() {
  FileProcess fp = new FileProcess();
  bool fromCall;
  fromCall = fp.FileExists(_GoodFileName);
  Assert.AreEqual(true, fromCall);
}

Create / Delete File

Instead of you having to create the file name in the location specified, prior to running the FileNameDoesExist test, you should create the file name within the method itself. You should delete the file after you have performed the FileExists call so you don’t keep files around you don’t need. Modify the FileNameDoesExist method to look like the following.

[TestMethod]
public void FileNameDoesExist() {
  FileProcess fp = new FileProcess();
  bool fromCall;
  if (!string.IsNullOrEmpty(_GoodFileName)) {
    // Create the 'Good' file.
    File.AppendAllText(_GoodFileName, "Some Text");
  }

  fromCall = fp.FileExists(_GoodFileName);
  // Delete file
  if (File.Exists(_GoodFileName)) {
    File.Delete(_GoodFileName);
  }

  Assert.IsTrue(fromCall);
}

TestContext

When the unit test framework creates an instance of a test class (a class marked with the [TestClass] attribute), the test framework creates a TestContext object. This object contains properties and methods related to testing. To access this TestContext object, you must create a public property named TestContext in each of your test classes. The test framework checks for this property and inserts the instance of this TestContext into your property.

private TestContext _TestInstance;  
public TestContext TestContext  
{  
    get { return _TestInstance; }  
    set { _TestInstance = value; }  
}

One of the things you can do with this TestContext is to use the WriteLine method to add some output into the test results. Add the lines shown in bold below to write some messages about what file you are creating into the output area of the test results.

[TestMethod]
public void FileNameDoesExist() {
  FileProcess fp = new FileProcess();
  bool fromCall;
  if (!string.IsNullOrEmpty(_GoodFileName)) {
    TestContext.WriteLine("Creating file: " + _GoodFileName);
    // Create the 'Good' file.
    File.AppendAllText(_GoodFileName, "Some Text");
  }
  TestContext.WriteLine("Checking file: " + _GoodFileName);
  fromCall = fp.FileExists(_GoodFileName);
  // Delete file
  if (File.Exists(_GoodFileName)) {
    TestContext.WriteLine("Deleting file: " + _GoodFileName);
    File.Delete(_GoodFileName);
  }
  Assert.IsTrue(fromCall);
}

Run this test, once it is complete, click on the FileNameDoesExist test in the Test Explorer window, locate the Output link at the bottom of the window and click on it. You should then see your messages appear in an output window as shown in Figure 1.


Figure 1: Write output messages using the TestContext property

Summary

In this blog post a constant was used in place of a hard-coded file name. You placed another hard-coded file name into the App.config file in the test project. Within a test method you created a file, tested that file’s existence, then deleted the file. This helps keep that method self-contained and avoids manual setup of files prior to running these tests. Finally, you added a TestContext property to access the WriteLine method of the TestContext property. This allows you to add additional messages into the output of the test results.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Avoid Hard-Coding in Unit Tests.