July 2017

Volume 32 Number 7

[ASP.NET Core]

ASP.NET Core with Angular, Web API and Azure DocumentDB

By Chander Dhall

With more people migrating to .NET Core and from AngularJS 1.x to Angular 2+, these technologies have become essential, along with other Microsoft technologies in Web API and Azure DocumentDB.

The Yeoman generator aspnetcore-spa (bit.ly/2q10dFn) supports JavaScript frameworks such as Angular, React, and VueJS, as well as other features such as Webpack (bit.ly/2osyQXJ) and Hot module replacement (bit.ly/2oKsKNs). Yeoman was used to start the project I’ll be discussing in this article. Note that the current generator defaults to a Visual Studio 2017 project.

The code discussed in this article can be found at bit.ly/2q1aSzR. There, you’ll find a basic shopping cart implemented using ASP.NET Core, Angular, Web API and Azure DocumentDB.

Web API

A solution that tends to get overlooked but plays a crucial role is the Web API. Using Visual Studio, I created an empty API Controller. You’ll notice upon creation inheriting from the Controller and a Route attribute is decorating the API Controller class:

[Route("api/Carts")]
public class CartsController : Controller {}

API Controllers no longer inherit from APIController, but from Controller, just like an MVC Controller. The differences between the old API Controller and MVC Controller are minimal. In fact, in previous versions of MVC, some people used MVC Controllers as Web API Controllers by simply returning a JsonResult in their action.

Attribute Routing has been supported in the past. Scaffolding has it now and is called Route instead of RoutePrefix.

Utilizing Attributes

In your empty controller, you only have the need for some simple CRUD operations, so you’ll create a simple GetById action method:

public Cart GetById(int id){ return _carts.FirstOrDefault(x => x.Id == id); }

Using default Web API routing conventions, you can get Cart with Id=5 by making a call to the route api/Carts/5. This is a commonly used route template constructed by the route prefix defined earlier in your class; this action method took an int only as a param­eter. One way you can identify which HTTP verbs are needed to call this method is to look at the prefix of the action method itself (GetById). Here, the prefix is identified to use a GET call, for the same applies to PutCartItem, PatchCartItemQuantity, DeleteCartItem and PostCheckout.

Those last few action methods could work, but even if they did, how readable would they be? And more important, if a new person on your team looked only at PostCheckout, he might not know it refers to a POST call to submit your cart for checkout, or if he would interpret the method as called after (or post) you had already checked out. For readability and to make your method more developer-friendly, you’ll decorate your method with attributes.

Verb attributes give developers the ability to decorate action methods to specify what HTTP actions can be performed. With the GetById method, you can make it so that only GET calls can access it:

[HttpGet]
public Cart GetById(int id){ return _carts.FirstOrDefault(x => x.Id == id); }

Attribute Routing takes precedence over naming by convention. For example, if you decide to change the name of the method from GetById to PutById, it would still only allow the HTTP GET verb. Additionally, more than one verb can be applied on a method. In the following example you can see two different ways of doing that. Other than personal preference on how you want to code it, there’s no difference between those two implementations:

[HttpGet]
[HttpPost]
public Cart FunctionName(int id){ //Implementation }
[HttpGet, HttpPost]
public Cart FunctionName (int id){ //Implementation }

Route Attributes help when Web API Methods start to become more complex. It’s also good practice to use them on the simple methods, as well, because they’ll continue the process of making methods more readable and, at the same time, create more constrained protection from adverse calls.

As a reminder, because the controller is already decorated with a route prefix, you don’t need to repeat that at the method level. This information makes the route attribute on your current method simple. The curly brackets in your template identify that a parameter is to be passed into your route. Previously, this was the number 5:

[HttpGet]
[Route("{id}")]
public Cart GetById(int id){ return _carts.FirstOrDefault(x => x.Id == id); }

The name of the template parameter is important because that tells you that you need to have a parameter on your method with the same name. The following API method is a valid call from “api/Carts/5.” The only problem is that cartId will be 0 and not 5 because the value 5 is in a parameter with the name “id”:

[HttpGet]
[Route("{id}")]
public Cart GetById(int cartId){ return _carts.FirstOrDefault(x => x.Id == id); }

API route template parameter constraints help filter which calls get made in a particular method. These constraints are important because you don’t want your method doing too much. Web API methods should be simple and bring back specific data, not a bunch of data that the client will then have to filter and dig out for information. By specifying the int constraint “{id: int}” on the template parameter, you specify that this method will only be called by a route that has an integer in this template position. Now, if you decide to create a GetByName method, you know which method will be called for each, and more important, you don’t have to put logic into your method to determine if the parameter passed in was an integer or string value.

Route templates can be attached to a method in a few different ways. All of the following code examples will perform the same way:

[HttpGet]
[Route("{id: int}")]
public Cart GetById(int cartId){ return _carts.FirstOrDefault(x => x.Id == id); }
[HttpGet, Route("{id: int}")]
public Cart GetById(int cartId){ return _carts.FirstOrDefault(x => x.Id == id); }
[HttpGet("{id: int}")]
public Cart GetById(int cartId){ return _carts.FirstOrDefault(x => x.Id == id); }

The Return Type specified doesn’t always have to be the actual class/value type you’re returning. In many cases, your method will have more flexibility if you return IActionResult.

Your GetById method would actually benefit from this return type. When a cart can’t be found, currently null is passed back to the client, but as a success. You don’t want your client to have to perform that check, nor do you want your clients to have to deter­mine in their ajax catch if this call had a data problem or a code problem, which could affect any messaging relayed to the user. You pass back built-in result objects, which automatically create the appropriate response message and status code:

[HttpGet, Route("{id: int}")]
public IActionResult GetById(int cartId){
  var cart = _carts.FirstOrDefault(x => x.Id == id);
  if (cart != null) return Ok(cart);
  return BadRequest();
}

Another added benefit of passing back IActionResult is that it doesn’t care what data type you pass back within the result object. If you had a method GetItem, you could return Ok(new ObjectA()) or Ok(new ObjectB()). The type returned within the OkObjectResult here is of different types. 

Waiting … Waiting … Waiting …

Your current Web API method is now in good shape, but it isn’t streamlined. It’s synchronous and still holding onto threads. You need to not only use async/await, but make sure you take in a CancellationToken parameter, as well. Using an asynchronous call lets you have more throughput come into your application, but if a process starts taking a long time and a user refreshes the page, navigates away, or closes the browser, that process still runs and occupies resources that could be allocated for another user. That’s why you always should implement a CancellationToken for every asynchronous GET method at a minimum. Adding in a CancellationToken parameter to an API method won’t affect your route template. It will give you a token that you can pass down to an appropriate async call to handle:

[HttpGet, Route("{id: int}")]
public async Task<IActionResult> GetByIdAsync(int cartId, CancellationToken token){
  var cart = _cartService.GetByIdAsync(cartId, token);
  if (await cart != null) return Ok(cart);
  return BadRequest();
}

Tools

Now that you have the Web API in place, you don’t have to build out the UI or your own tool to test the Web API functionality. Along with your unit tests, two tools stand out as great utilities to support your API development: Postman (bit.ly/19MnN02) and Swagger (bit.ly/2p1GeYH).

Web API has other features to make Web API robust and help keep your API methods down to as few lines of code as possible. In your empty controller, you had only the need for some simple CRUD operations, but other APIs you’ve created have put a message on an Azure Queue, kicked off a back-end process, or fired and forgotten as part of CQRS. The application of using API Controllers is vast and extensive.

Angular

Today’s Web APIs are utilized frequently by one of the many JavaScript frameworks that companies use. One of the more popular frameworks is AngularJS. AngularJS provides some great features and lets a development team get up and running quickly. AngularJS was completely rewritten from version 1.x (called AngularJS) to version 2.x (all versions starting from version 2 are now called Angular), and the Angular team recently released version 4.0, which is not a rewrite and is backward-compatible to version 2.x (for the most part it’s a seamless upgrade). From a Visual Studio perspective, Angular can be intimidating because the setup isn’t as easy as it once was with just JavaScript files. Other technologies it helps to be familiar with are Webpack (bit.ly/2p1Jfs5), TypeScript (bit.ly/2p1LBXN), npm (bit.ly/2pfw316) and Reactive Extensions for JavaScript (rxjs) (bit.ly/2piytxU).

Modules

The building blocks of all Angular applications are Modules. You can compare Modules to an MVC Area. For the most part, it’s segregated from the rest of the application, and different developers can work on different modules at the same time with barely any overlap. In the code provided on GitHub, there are three modules of importance: AppModule, ProductsModule and CartsModule. ProductsModule and CartsModule are the two that are isolated-like areas. AppModule is the connector as it puts everything together. 

Components

A module itself is typically made up of other modules, components and services. The components declared in each module are where the data and HTML come together.

Through Visual Studio, you create a simple component (products.component.ts) under your Web project (MainSite/ClientApp/app). The initial creation of this component can be seen in Figure 1.

Figure 1 Simple Angular Component: ProductsComponent

import { Component } from '@AngularJS/core';
@Component({
  selector: 'products'
  template: `
    <h2 class="intro-header">List of Products</h2>
    <ul>
      <li>I am a product</li>
      <li>I am another product</li>
    </ul>           
  `,
  styles: ['.intro-header { color: blue }', 'p {font-size: 10px;}'],
  providers: []
})
export class ProductsComponent {}

Component Decorator

Other than the @Component decorator, this TypeScript looks similar to a C# class with the references at the top and class being declared. The @Component decorator has a required object, which tells Angular how to use this class and lets the component be self-contained.

The selector informs Angular that in any HTML if this CSS selector is used, it should create this component and insert its generated HTML at that location. This also means that if you have HTML code like the following, you’ll have three separate instances of each component and three separate classes will be created:

<products></products>
<products></products>
<products></products>

The template allows HTML to be inside a single file rather than always having to generate an HTML file for every view. As the current template wraps lines, single quotes aren’t used. Instead, tick marks are created and it’s a good habit to use even if the template is just a single line where single quotes could be used.

If your HTML gets a little messy, you can always pull your template out into its own file and reference it, but template now becomes templateUrl. The same can be said for styles, except that it’s an array of styles or it’s an array of references to styleUrls.

Providers is an array where Injectable Services for your component class can be declared. Singleton services would not be declared at the component level. Each time a component gets created, Angular does an inside-out search for the injected services. Looking at Figure 2, you’d never realistically create this scenario because re-registering providers is typically not a good thing, but this scenario illustrates the point. If Component_A has a WidgetService injected into it, Angular first looks inside Component_A. Identifying that the WidgetService is there, Component_A will get a new instance of WidgetService every time it’s created. You could have two Component_A classes each with their own WidgetService in which those two services know nothing about each other. Moving on from there, Angular would find a WidgetService for Component_B at Child.Module_A. This service is a singleton for all components under Child.Module_A, which do not provide their own WidgetService. Last, a WidgetService for Component_C is found at the Parent.Module. A service placed out at the Parent.Module (or AppModule) level is traditionally used as a singleton for the entire Single Page Application.

Hypothetical Layout of Parent Modules, Child Modules and Their Components
Figure 2 Hypothetical Layout of Parent Modules, Child Modules and Their Components

Component Class

As mentioned, component classes are very similar to the C# classes with which most developers are familiar. They have constructors where services can be injected, as well as parameters declared inside and outside of methods. One added benefit that Angular provides is lifecycle hooks. Figure 3 shows a list of all the lifecycle hooks that are able to be tied into. Two of the most popular are ngOnInit and ngOnDestroy.

Angular Lifecycle Hooks Provided by Angular Documentation
Figure 3 Angular Lifecycle Hooks Provided by Angular Documentation

ngOnInit tends to be the place where initial service calls are made to obtain initial data. This hook is only called once and, more important, any @Input variables have been set at this time.

ngOnDestroy is a great place to unsubscribe from subscriptions that don’t self-terminate. If you don’t unsubscribe from these subscriptions, it could potentially cause memory leaks.

Template Syntax

HTML Template Syntax provides a way for components to not only affect the look and feel of its own content, but also for components to talk to each other. Following is some of the most commonly used syntax.

Interpolation uses curly braces ({{}}) to extract the content of the object property:

@Component({
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <span class="form-control-static">{{product.name}}</span>
      </div>
    </div>
  `
})
export class ProductComponent { product: Product = new Product("Computer ABC"); }

One-way binding lets you set values from a component object/property as an attribute of an HTML element. Here, you designate the value as being one way bound by enclosing that attribute with brackets ([]):

@Component({
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <input type="text" class="form-control" disabled=""
          [value]="product.name" />
      </div>
    </div>
  `
})
export class ProductComponent { product: Product = new Product("Computer ABC"); }

Two-way binding is similar to one-way binding except that as the value of the HTML element changes, the value in the component changes, as well. In Figure 4, two-way binding by specifying (ngModel) is designated. In this case, ngModel is used because it’s an internal Angular directive for form elements. Because all (boundName) does is comb both one-way binding [] and event binding (), you can use this syntax on any element as long as it supports both the ability to have a value set on boundName and a boundNameChange event.

Figure 4 Example of Two-Way Binding

@Component({
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <input type="text" class="form-control" [(ngModel)]="product.name" />
        <span class="form-control-static">{{product.name}}</span>
      </div>
    </div>
  `
})
export class ProductComponent { product: Product = new Product(); }

Event binding binds a particular action to an HTML element and designates the action upon this event happening. Figure 5 shows an example of event binding where a (click) event calls handleButtonClick.

Figure 5 Example of Event Binding

@Component({
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <span class="form-control-static">{{product.name}}</span>               
      </div>
    </div>
    <div class="row">
      <div class="col-md-12">
        <button type="button" class="btn btn-primary"
          (click)="handleButtonClick();">Click Me</button>
      </div>
    </div>
  `
})
export class ProductComponent {
  product: Product = new Product("Computer ABC");
  handleButtonClick(): void { this.product.name = "Comptuer XYZ"; }
}

*ngIf lets you display content conditionally:

@Component({
  template: `
    <p *ngIf="product.name === 'Computer ABC'">Computer name is "Computer ABC"</p>
    <p *ngIf="product.name === 'Computer XYZ'">Computer name is "Computer XYZ"</p>
  `
})
export class ProductComponent { product: Product = new Product("Computer ABC"); }

*ngFor provides you the ability to look over an array of objects, as shown in Figure 6.

Figure 6 Example of *ngFor

@Component({
  template: `
    <div class="row" *ngFor="let product of products; let i = index;">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <span class="form-control-static">{{product.name}}
          with Index: {{i}}</span>               
      </div>
    </div>
  `
})
export class ProductsComponent {
  products: Product[] = [new Product("Computer ABC"),
    new Product("Computer XYX") ];
}

Pipes take your data and manipulates it. Some Angular pipes are | date, | currency, | uppercase. Custom pipes can be created, too. In Figure 7, | async is used. This pipe is useful when handling data from an Observable. Products is an Observable, and you don’t have to call subscribe on getProducts because that’s handled by Angular.

Figure 7 Example of | async

@Component({
  template: `
    <div class="row" *ngFor="let product of products | async; let i = index;">
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <span class="form-control-static">{{product.name}} with Index: {{i}}</span>
      </div>
    </div>
  `
})
export class ProductsComponent implements OnInit {
  products: Observable<Product[]>;
  constructor(private productService: ProductService) { }
  ngOnInit() { this.products = this.productService.getProducts(); }
}

Communication between parent components and child components is interesting in Angular. To help ease that burden, Angular provides @Input and @Output decorators.

@Output decorator provides the opportunity to emit events up a level to the parent, as shown in Figure 8.

Figure 8 Example of @Output Decorator

@Component({
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <label>Button Action:</label>
        <span class="form-control-static">{{buttonAction}}</span>               
      </div>
    </div>
    <buttons (buttonClicked)="handleButtonClicked($event)"></buttons>
  `
})
export class ProductsComponent {
  buttonAction: string = "No Button Pressed";
  handleButtonClicked(actionTaken: string): void { this.buttonAction =
    actionTaken; }
}
@Component({
  selector: 'buttons',
  template: `
    <div class="row">
      <div class="col-md-12 form-group">
        <button type="button" class="btn btn-primary"
          (click)="save()">Save</button>
        <button type="button" class="btn btn-default"
          (click)="remove()">Remove</button>
      </div>
    </div>
  `
})
export class ButtonsComponent {
  @Output() buttonClicked = new EventEmitter();
  save() { this.buttonClicked.emit('Save Clicked'); }
  remove() { this.buttonClicked.emit('Delete Clicked'); }
}

Likewise, @Input decorators are used to populate a child component from a parent component, as shown in Figure 9.

Figure 9 Example of @Input Decorator

@Component({
  template: '<product-info [productDetails]="product"></product-info>'
})
export class ProductsComponent { product: Product = new Product("Computer ABC"); }
@Component({
  selector: 'product-info',
  template: `
    <div class='row'>
      <div class="col-md-12 form-group">
        <label>Product Name:</label>
        <span class="form-control-static">{{productDetails.name}}</span>               
      </div>
    </div>
  `
})
export class ProductComponent { @Input() productDetails: Product; }

If a more robust notification is needed, you can build an internal notification system where other components can subscribe to Observables defined by this internal system.

Other IDEs

Angular completely separated from the Visual Studio build process is advantageous because the code itself can be pulled out and used in other editors such as WebStorm (bit.ly/2oxkUeX) and Sublime (bit.ly/1SuiMgd).

DocumentDB

DocumentDB is a fully managed NoSQL database service built for fast and predictable performance, high availability, elastic scaling, global distribution and ease of development. As a schema-free NoSQL database, DocumentDB provides rich and familiar SQL query capabilities with consistent low latencies on JSON data, ensuring that 99 percent of your reads are served in less than 10 ms and 99 percent of your writes are served in less than 15 ms.

DocumentDB transparently replicates your data to all regions you’ve associated with your DocumentDB account, enabling you to develop applications that require global access to data while providing tradeoffs between consistency, availability and performance, all with corresponding guarantees. DocumentDB provides transparent regional failover with multi-homing APIs, and the ability to elastically scale throughput and storage across the globe.

The core promise of DocumentDB is its service-level agreements (SLAs), which are highly rated in the industry for throughput, latency, availability and consistency. This is a unique offering in the database industry. DocumentDB provides these guarantees to help alleviate the challenges faced by developers who need to develop low latency, distributed, highly available applications.

Even though it may not be related to the application you’ve built, it’s good to know that DocumentDB provides four consistency levels:

Strong: This is an RDBMS-like consistency. With every request, the client is always guaranteed to read the latest acknowledge write. However, this is slow and in order to use this, the DocumentDB account cannot be associated with more than one region.

Bounded staleness: This level guarantees that the reads may lag behind by at most x versions of the document or a certain time interval by the client. So, if the client sets x=2, the user will be guaranteed to get a document no later than the last two versions. This functions the same with time. If the time is set to five seconds, every five seconds the resource will be guaranteed to have been written to all replicas to make sure that subsequent requests can see the latest version.

Session: This is the most popular of all, and as the name suggests, is scoped to a client session. Imagine someone added a comment on a product on an eCommerce Web site. The user who commented should be able to see it; however, it will take some time before other users on the Web site can see it, too.

Eventual: As the name suggests, the replicas will eventually converge in absence of any additional writes. Eventual consistency provides the weakest read consistency, but offers the lowest latency for both reads and writes.

Architecture

DocumentDB Account has multiple databases. The database can be reached via the Uri [AccountUri]/ dbs/{id}, where AccountUri is of the pattern https://[account].documents.azure.net and whose database has the following collections (collections can be reached with the Uri [AccountUri]/dbs/{id}/colls/{id}):

  • Documents—can be reached with the Uri [AccountUri]/ dbs/{id}/colls/{id}/docs/{id}
  • Attachments—can be reached with the Uri [AccountUri]/ dbs/{id}/colls/{id}/docs/{id}/attachments/{id}
  • Stored Procedures—can be reached with the Uri [Account­Uri]/ dbs/{id}/colls/{id}/sprocs/{id}
  • Triggers—can be reached with the Uri [AccountUri]/ dbs/{id}/colls/{id}/triggers/{id}
  • User Defined Functions—can be reached with the Uri [AccountUri]/ dbs/{id}/colls/{id}/functions/{id}
  • Users—can be reached with the Uri [AccountUri]/ dbs/{id}/users/{id}. Users have permissions that can be reached with the Uri [AccountUri]/ dbs/{id}/users/{id}/permissions/{id}

The unit of record is a Document, and a collection is just as the name sounds: a collection of documents. Because documents are flat, it’s better to think of them as flat objects and not like rows in a table. Coming from the SQL world, there’s a tendency to think of a collection as a table and documents like rows. However, that analogy has more problems than you might realize, especially when it comes to designing the architecture and later implementing it.

DocumentDB automatically handles all aspects of indexing. DocumentDB also supports specifying a custom indexing policy for collections during creation. Indexing policies in DocumentDB are more flexible and powerful than secondary indexes offered in other database platforms because they let you design and customize the shape of the index without sacrificing schema flexibility.

DocumentDB models JSON documents and the indexes as trees, and lets you tune to policies for paths within the tree. You can find more details in an introduction to DocumentDB indexing at bit.ly/2qg2Nqa. Within documents, you can choose which paths must be included or excluded from indexing. This results in improved write performance and lower index storage for scenarios when the query patterns are known beforehand.

In Figure 10, you see logical representation of the index for JSON documents.

Representation of the Index for JSON Documents
Figure 10 Representation of the Index for JSON Documents

Additional Info

Testing your query in DocumentDB is sometimes the hardest part of implementing it. Azure provides some ability to run queries through its portal, but I particularly like using DocumentDB Studio (studiodocumentdb.codeplex.com). Also, if you’re interested in checking out DocumentDB in more depth, checkout my Channel 9 Video on DocumentDB (bit.ly/2pJ6A2c).

Overall, Angular, Web API and Azure DocumentDB are each a great technology to have in your development arsenal, but combining them together, applications from as simple as hosting blog entries to as complex as an eCommerce Web site can be created. Figure 11 shows what can quickly be constructed when these three flexible and easy-to-implement technologies are used together.

Output of the GitHub Code, Built Using ASP.NET Core, Angular and DocumentDB
Figure 11 Output of the GitHub Code, Built Using ASP.NET Core, Angular and DocumentDB


Chander Dhall is a Microsoft MVP, Azure Advisor, ASP.NET Insider and Web API Advisor, as well as CEO of Cazton Inc. (cazton.com). He’s been active in speaking at top technical conferences around the world. Dhall also conducts workshops internationally to train top developers on ASP.NET Core, Angular, TypeScript, databases (both NoSQL and SQL) and many other technologies.

Thanks to the following Microsoft technical experts who reviewed this article: Govind Kanshi, Daniel Roth, Steve Sanderson and Ted Neward
Govind Kanshi (Microsoft) works on DocumentDB team as Principal Program manager. His focus is on data platform and in spare time his daughter Nishka keeps him busy with her youtuber videos.

Daniel Roth (Microsoft) is a Senior Program Manager on the ASP.NET team. In his spare time he enjoys spending time with his wife and two boys, Benjamin and Joshua.

Steve Sanderson (Microsoft) works on the ASP.NET team, focusing on making the platform great for JavaScript application developers.

Ted Neward is a Seattle-based polytechnology consultant, speaker and mentor. He has written more than 100 articles, is an F# MVP, and has authored and coauthored a dozen books.


Discuss this article in the MSDN Magazine forum