DPS907 notes – Thu Sep 14

Today, we learn how to code a controller to handle all typical HTTP methods. We also review how to work with associated entities, and their design and coding patterns.

 

Test today

Test 2 is today.

All tests in this course are hand-written. Pen or pencil. Answer styles will vary, but we’re mostly looking for short- to medium-length answers, and/or diagrams, and so on.

No references or resources are permitted – “closed book, open brain”. More information is on the graded work page.

The tested topics are mostly forward-looking. You will be tested on comprehension, understanding, and the application of the current week’s topics (Tuesday and Thursday). For this week, it’s mostly the notes and linked content below, and the posted code examples. Also, you may be tested on a past recently-covered topic, which enables you to show that you have the foundational knowledge needed for current and future tasks.

 

Routing in ASP.NET Web API

An ASP.NET MVC web app or web service uses the front controller pattern. (Read this Wikipedia overview article). Every request is inspected by a single module (in the ASP.NET runtime engine), and then dispatched to the code module that can handle the request (which is usually a controller).

The dispatch destination is defined by routes. Routes are configured in a class, in the project’s App_Start folder:

  • Web app routes are in the RouteConfig.cs source code file
  • Web service routes are in the WebApiConfig.cs source code file

By default, web service request URIs include an “/api/” segment, although we can change that default in the future.

Then, by default, the next segment of the route designates the controller that will handle the request.

The HTTP request method, along with the presence of a query string, and/or an entity body, will then determine the method in the controller that handles the request.

Routing basics are covered in this ASP.NET Web API article. Study the other articles in the “Routing” collection of articles.

 

Which controller method executes?

You have learned that an ASP.NET Web API web service uses the front controller pattern to determine which method in which controller to run when a request is received.

How does the algorithm work?

Before we cover the steps, think about the data that could be part of a request. A request could include simple data types, and/or complex data types.

Simple data types – in the URI and/or query string – include:

  • int, single, double, decimal
  • string, byte, char, bool, guid
  • datetime, timespan

Complex data types – in the message body – include:

  • anything else; typically any instance of a class
  • your own classes, or .NET Framework classes
  • one-of, or collections-of

Here’s the algorithm:

1. The URI is inspected, looking for a controller name match

2. The HTTP method is inspected

3. The request’s data is inspected – data could be part of the URI and/or query string, or in the message body

Interestingly, these two URIs are the same, for a ‘get-one’ request:

http://example.com/api/foo/3

http://example.com/api/foo?id=3

Then:

4. Identify all controller methods that match the HTTP method name (e.g. ‘GetSomething‘)

5. Match the simple parameter data types – that should be enough to uniquely identify the controller method

If step 5 above yields more than one match, the runtime throws an exception, with a useful message that tells you how to improve your code to avoid the problem in the future.

 

Documentation

Mike Wasson has two articles that provide more detail, on the official asp.net/webapi web site:

Routing in ASP.NET Web API

Routing and Action Selection

 

Serialization and model binding

On the ASP.NET Web API web site, there’s a collection of articles on serialization and model binding.

By default, the Web API project template can handle three internet media types:

  • JSON – application/json – in and out
  • XML – application/xml – in and out
  • Form data (i.e. data from HTML Forms) – application/x-www-form-urlencoded – incoming only

Data in-and-out of  the web service is handled by serializers. Formally, serialization is done in a media formatter, which is a class that plugs into the request-processing pipeline.

Serialization for JSON and XML is introduced in this ASP.NET Web API article. Please note that the article covers some scenarios that we may not encounter, because we use resource (view) models to overcome limitations and potential problems.

 

Model validation

Incoming data is validated against a resource (view) model, just like in ASP.NET MVC web apps. The School of ICT recommended app design process is to use resource (view) models for all data used when interacting with users (human or programmatic). Continue to use resource models, and ModelState.IsValid in your code.

Read this ASP.NET Web API article on model validation.

 

Content negotiation

Which media type gets returned? It depends, on several factors. Read this ASP.NET Web API article on content negotiation (often abbreviated as “conneg”), and think about the different use cases.

In RFC 7231, content negotiation is specified in Section 3.4.

 

Data annotations in web services

Most students in the course are familiar with data annotations. The second-year ASP.NET MVC web apps course introduces data annotations early in the course, and students use them in all their work for the remainder of the academic term.

If you need a refresher, then review the web app class notes page, in the section titled:

Data annotations overview

The notes linked (above) use web apps terminology, so it uses “view models”. Remember, in web services, we typically call these kinds of classes “resource models”.

They’re the same: resource model = view model.

 

System design guidance and how-to

During this course, you will learn to create a web service that has some level of sophistication. Therefore, you will need some guidance on how to design the system.

In your second-year web app programming and systems analysis courses, you used a “system design guidance” diagram to help you learn a set of topics. We will use a version of that diagram that’s customized for a web service. Click to open the PDF in a new tab/window:

System Design Guidance V7 for ASP.NET Web API web service

 

Supporting all relevant HTTP methods

Section 4 of RFC 7231 specifies the HTTP request methods. Please read/skim sections 4.1 and 4.2 now.

As you can see in Section 4.3, there are many methods available. In this web services course, you will learn to work with a subset of methods, which we call the “relevant” methods.

Our code example today (AllHttpMethods) will help you learn to support all relevant HTTP methods.

In addition, you will learn to create the correct response, which will be an error (as noted in a later section), or one of the results in the following table.

Request Return type Status code Comment
GET collection Collection 200
GET collection Empty collection 200 “Empty” is not an error condition
GET one One 200
GET one No item 404 Truly is ‘not found’
POST add item The added item 201 Added item typically includes an identity property
PUT item updated The updated item 200
DELETE item (no content) 204 The response body for DELETE requests have not been defined

 

Coding principles

In the later section (“HTTP status codes for errors”), you will learn that our manager methods will return data, or null.

In our controller methods, the return types will be as follows:

  • Get all – return an entity collection, typically in an IHttpActionResult
  • Get one, add new, update existing – return IHttpActionResult
  • Delete one – return void, which automatically returns HTTP status code 204

The “get all” case will always be successful. That’s why we can set the method’s return type as IHttpActionResult.

The “delete one” case may succeed, or fail. We don’t care at the controller level. By convention, a void return type returns 204.

For the others, the method’s return type will be IHttpActionResult. Always.

Review the section “Return type of a controller method” in the September 5 class notes.

 

For the following explanations, the week 2 code example named “AllHttpMethods” will be used. It has an entity named “Customers” that you can interact with.

 

“Get one” explained 

In the manager, “get one” uses the object’s identifier. If found, return the appropriate resource model type. Otherwise, null.

In the controller, call the manager method.

If the return value is null, return HTTP 404, “not found”.

Otherwise, return an Ok() result, along with the object.

 

“Add new” explained 

In the manager, “add new” attempts to add a new object. The object’s data passed input data validation in the controller, so the attempt should succeed. In the future, you can add business logic here if you need additional validation to be done.

Return the appropriate resource model type.

In the controller, you must first check the model state (i.e. input data validation). If the data is bad, return BadRequest().

If you have a well-designed resource model class, and use data annotations, the quality of the input data rises.

In addition, this step guards against under-posting (missing data), and over-posting (too much data).

 

If successful, call the manager method. Then, return a Created() result, along with the new object.

The Created() result requires you to supply a URI for the new object. The runtime will use that URI as the value of the Location header. To construct a URI, use the “new Uri” constructor. The Url.Link method is a helper method (System.Web.Http.Routing.UrlHelper). The method takes two arguments:

  1. A route name, which can be seen in App_Start/WebApiConfig.cs, and
  2. A route value, which we get from the new object’s “Id” (identifier) property

The “route name” will resolve to a URI that effectively is the base URI to this controller’s actions. The “route value” is then appended.

Alternatively, you could just build the URL segment as an interpolated string, for example:


var u = $"/api/customers/{addedItem.CustomerId}";

 

“Update existing” explained

In the repository, “update existing” first attempts to fetch the existing object. If the existing object does not exist, null is returned.

Alternatively, if successful, it has a reference to the object, and will proceed to update the object, and then return the updated object:

  1. ds.Entry(p) is the reference to an object “p”
  2. .CurrentValues is the object’s collection of current values
  3. .SetValues(updatedP) will update/change those values with the new supplied values

The SetValues method ignores missing properties, as well as navigation properties to associated objects.

In the controller, you must first check the model state. At the same time, for “update existing” scenarios, you must ensure that the identifier on the URI matches the identifier in the request entity body. If the data is bad, return BadRequest()..

This step guards against some kinds of attacks.

 

Alternatively, if successful, call the manager method. If successful, return an Ok() result and the updated resource.

 

“Delete existing” explained

In the manager, “delete existing” first attempts to fetch the existing object. If the existing object does not exist, do nothing (but in the future, we’ll fix that).

Alternatively, if successful, it attempts to remove the existing object from the collection. This attempt is guarded by a try-catch block. The attempt may fail, because the existing object may be associated with other objects.

In the controller, we simply call the manager method. We don’t care if the object exists, because we don’t want to reveal too much information (to the requestor) about our entity collection.

 

Associated (related) data introduction

Most students in the course are familiar with associations between objects. Today’s coverage will refresh your memory.

As you review this topic and concept, please remember that we are working with data in memory. While the source of the data is often a database, and the destination is an HTML document, we do not care about database concepts. Instead, we focus on working with data in memory.

This is important. We do not care about foreign keys, joins, and so on.

When working with data in memory, we are working with objects and collections of objects, and LINQ.

 

Real-life and software objects can have associations (or relationships) between them. There are several kinds of associations that we will work with:

  • one to many
  • many to many
  • one to one
  • to one, self-associated
  • to many, self-associated

 

One to many

You will often model real-life objects that have associations with other objects, in a one-to-many relationship.

For example:

  • “Program” has a collection of “Course” objects,
  • “Supplier” has a collection of “Product” objects, and
  • “Manufacturer” has a collection of “Vehicle” objects.

Looking at the other side of the association:

  • “Course” object belongs to a “Program”, and similarly,
  • “Product” is made by a “Supplier”, and
  • “Vehicle” is made by a “Manufacturer”.

 

Many to many

In real life, there are many situations that have a many-to-many association.

Consider your role as a student here at Seneca:

  • A “Class” (like DPS907 or WSA500) has a collection of “Student” objects, and
  • A “Student” has a collection of “Class” objects.

Now, think about the faculty employees here in the School of ICT:

  • An “Employee” (e.g. Peter McIntyre, Ian Tipson) can have many (a collection) of “Job Duty” objects, and
  • A “Job Duty” (e.g. teacher, coordinator, researcher) can be done by many (a collection) of “Employee” objects.

 

The others – one to one, self-referencing to-one and to-many

We will cover these kinds of associations in the near future.

 

Are associations always required?

When you are writing entity classes, are associations required between or among all classes?

No.

It is not necessary to associate every class to another. Do not try to do so, or force unnatural assocations. Incidentally, entity classes that are “standalone” and with no associations are used as an often-used technique for data that is used for lookup or validation purposes.

 

Declaring a navigation property

When writing data classes, the associations are simply coded as properties. However, they’re called “navigation properties“.

Definition reminder…

A navigation property has a data type of another class in the model.

 

ALWAYS add a navigation property to both associated classes.

Declare a to-one navigation property with the data type of the associated class. For example, if a Product is sold by a single Supplier:

[Required]
public Supplier Supplier { get; set; }

Declare a to-many navigation property with a collection type of the associated class. For example, a Supplier sells a number of Product items:

// In a design model class...
public ICollection<Product> Products { get; set; }

// In a view model class...
public IEnumerable<Product> Products { get; set; }

(Remember, whenever you declare a collection property, you MUST initialize it in a default constructor, most often as a List<TEntity>.)

 

Setting the value of a navigation property

Before setting the value of a navigation property, you must have a reference to an object (of that data type). Although you can create a new object in the statement, you will often have another existing variable for (or is a reference to) the object.

Set a to-one property as follows. Assume that “walmart” is a Supplier object, and “shirt” is a Product object:

// shirt is a Product object
// walmart is a Supplier object
shirt.Supplier = walmart;

Setting a to-many property requires some thought. Do you want to add a new object to the collection? Do you want to replace the existing collection with a new collection? Here are some examples, using the same “walmart” and “shirt” objects, but we also have a collection of toaster objects named “toasters”:

// add a new object to the collection
walmart.Products.Add(shirt); 

// replace the existing collection with a new collection
walmart.Products = toasters; 

Important: When you write a statement in code to set one end of the association, do you need to write another statement to set the other end?

When we use a persistent store (SQL Server via the Entity Framework), NO, we don’t have to. In this scenario, when you set one end of the association, the persistence framework sets both ends of the association.

 

Getting (reading) the value of a navigation property

Getting the value of a navigation property is natural and simple. Just pay attention to data types (an object, or a collection of objects).

Also, when you have a to-one association, it’s easy to “walk” the object graph to get access to properties in the associated object. For example, using the “shirt” object from above, you could get the “Name” or “Address” of the supplier:

string supplierName = shirt.Supplier.Name;

 

How to design your resource (view) model classes
so that they include an associated object or collection

At this point in time, you have learned how classes can be associated with one another, by using navigation properties.

What about resource model classes?

Well, we use navigation properties in any class that describes a real-world object – design model class, or resource model class.

Do we add navigation properties to ALL resource model classes?

No.

Add them only when it makes sense. Often, the “Base” resource model class is intended to hold all, or almost all, of the properties defined in the design model class. You may be tempted to add a navigation property to the “Base” class. Resist that temptation.

Instead, create another resource model class, with the desired navigation property. It’s OK to use inheritance to simplify coding.

Let’s look at three scenarios from the code example. The problem domain is a one-to-many association, of recent National Football League playoff teams, and players. A Team has a collection of Players. Conversely, a Player belongs to a Team.

This resource model class uses the familiar “Base” style, for the Team entity. For clarity, the data annotations were removed.

public class TeamBase
{
  public TeamBase() { }

  public int Id { get; set; }
  public string CodeName { get; set; }
  public string Name { get; set; }
  public string City { get; set; }
  public int YearFounded { get; set; }
  public string Conference { get; set; }
  public string Division { get; set; }
  public string Stadium { get; set; }
}

 

The following is the recommended way to add the navigation property for the collection of Player objects. Notice the name of the resource model class, and that it inherits from “Base”. Notice also that the collection type, in a resource model class, is IEnumerable<TEntity>. Two more items to notice:

  1. The data type (of the navigation property) is a collection of a resource model class type
  2. The name (of the navigation property) exactly matches the name in the design model class

 

public class TeamWithPlayers : TeamBase
{
  public TeamWithPlayers()
  {
    Players = new List<PlayerBase>();
  }

  public IEnumerable<PlayerBase> Players { get; set; }
}

 

The next resource model class uses the familiar “Base” style, for the Player entity. For clarity, the data annotations were removed.

public class PlayerBase
{
  public PlayerBase()
  {
    BirthDate = DateTime.Now.AddYears(-25);
  }

  public int Id { get; set; }
  public int UniformNumber { get; set; }
  public string PlayerName { get; set; }
  public string Position { get; set; }
  public string Height { get; set; }
  public int Weight { get; set; }
  public DateTime BirthDate { get; set; }
  public int YearsExperience { get; set; }
  public string College { get; set; }
}

 

The following is the recommended way to add the navigation property for the Team object. Notice the name of the resource model class, and that it inherits from “Base”. Three more items to notice:

  1. The data type (of the navigation property) is a resource model class type
  2. The name (of the navigation property) exactly matches the name in the design model class
  3. The to-one navigation property almost always needs a “Required” data annotation

 

public class PlayerWithTeamInfo : PlayerBase
{
  [Required]
  public TeamBase Team { get; set; }
}

 

While the scenario above is common, sometimes you do not want, or need, the associated object. Instead, you need one or two properties from the associated object.

AutoMapper has a feature called “flattening” which helps with this scenario. For example, assume that we want only the Team’s “Name” and “CodeName” properties. Create new properties, with a composite name, Class + Property Name. The data type must match the data type of the property in the design model class.

Here’s what it looks like:

public class PlayerWithTeamNames : PlayerBase
{
  // Design model class name is "Team"
  // Property names are "CodeName" and "Name"
  public string TeamCodeName { get; set; }
  public string TeamName { get; set; }
}

 

AutoMapper and associated data

This is a good-news story.

If you define (create) maps from design model classes to resource model classes that have navigation properties, AutoMapper will work the same way that it does for other data types.

In other words, it works nicely with associated entities. The notes below discuss three scenarios:

  1. For an object, mapping an associated collection
  2. For an object, mapping an associated object
  3. Mapping the individual properties of a related object (in a ‘to-one’ association)

The scenarios below echo the guidance in the previous section (above).

 

How to use the Include() extension method

When you are writing the query to fetch an object (or collection) from the data store, the Include() extension method is used to additionally fetch an associated object or collection.

The Include() method accepts a string argument. What’s the string?

It is the name of the navigation property that is the associated object or collection. 

For example, using the design model classes above:

var o = ds.Teams.Include("Players").SingleOrDefault(t => t.Id == id);

It gets better: Assume that your data model is richer, with many entities, and long “chains” of associated objects. If you want to fetch the associated data from several entities in the same chain, the Include() method string can be a dot-separated list of the names of the navigation properties. For example, using the Chinook data model, assume that we want a specific invoice, and data from the associated customer, and employee. In this situation:

var o = ds.Invoices.Include("Customer.Employee").SingleOrDefault(i => i.Id == id);

 

For an object, mapping an associated collection

If you have an entity that has an associated (related) collection – for example, an academic Program has a collection of Courses, or a Supplier has a collection of Products – you may want to return an object with its associated collection.

To do this, you need to do four tasks:

1. In a new resource model class, add a navigation property for the collection. The type is a resource model class type.  The property name must match the property name in the design model class.

2. Define a map, from the design model class to the new resource model class that you created in step 1 above.

3. In the manager, when the code fetches the object, add the Include() extension method to fetch its associated collection.

4. In the controller, add a method that will call the manager method.

Here’s how these code changes work together. Click to open it in a new tab/window:

automappercollection

 

For an object, mapping an associated object 

If you have an entity that has an associated entity – for example, an academic Course is associated with a single Program – you may want to return an object with its associated object.

To do this, you need to do four tasks:

1. In a new resource model class, add a navigation property for the object. The type is a resource model class type. The property name must match the property name in the design model class.

2. Define a map, from the design model class to the new resource model class that you created in step 1 above.

3. In the manager, when the code fetches the object, add the Include() extension method to fetch its associated object.

4. In the controller, add a method that will call the manager method.

 

Mapping some individual properties of a related object (in a ‘to-one’ association)

If you have an entity that has an associated object – for example, an academic Course belongs to a Program, or a Product belongs to a Supplier – you may want to return individual properties from its associated object.

To do this, you need to do four tasks:

1. In a new resource model class, add a property with a type that matches the property in the associated type. The property name must be a composite of the design model class name and the property name.

2. Define a map, from the design model class to the new resource model class that you created in step 1 above.

3. In the manager, when the code fetches the object, add the Include() extension method to fetch its associated object.

4. In the controller, add a method that will call the manager method.

Here’s how these code changes work together. Click to open it in a new tab/window:

AutoMapperComposite

 

Delivering associated data to requestors

Study the code example (AssociationsIntro), as you review this section.

If a manager method creates an object graph that has one or more objects with data from associated objects, simply return the object graph from the controller method. The ASP.NET Web API serializer will construct the correct JSON.

In the controller, there are two typical ways to configure a request for a resource that includes associated data:

  1. Add a query string parameter to the URI (not recommended)
  2. Use the attribute routing feature (preferred, and covered next)

 

Attribute based routing

This feature is ON by default in our projects. We can use it in combination with the existing convention based routing.

This article by Mike Wasson (on the official ASP.NET Web API web site) fully explains attribute routing. Read it, and begin using the feature in your work.

In the AssociationsIntro code example, look for its usage in the Artists and Albums controllers. There are methods that enable a requestor to get an object (or collection) with, or without, the associated object(s).

 

Query techniques

In the previous day’s class notes, query techniques were reviewed. Make sure you are, or get, comfortable with query techniques.

 

Suggested query strategies for web apps and services

The following are suggested query strategies for web apps and services. These are not rules, they’re just suggestions. (And not quite as important as a best practice.) Please be flexible, and balance the needs of the use case with the cost of fulfilling the query.

Get all – We do not typically use the Include() method in a “get all” method. The main reason is data volume – it’s possible that the included data will be sizeable in relation to the size of the fetched collection. However, if you really need some or all of an included object, for example, then that may be acceptable. FYI, you can limit the size of the fetched collection by using the Take() method. In addition, there are advanced LINQ query expression components that will result in the generation of an efficient query at the database.

Get one – We often use the Include() method in a “get one” method. The included object/collection typically adds good value to the fetched object. Richer strings can be composed, and collection items can be rendered. Again, be flexible.

 

Additional background information:

Lazy loading, eager loading – what are these terms?

When working with associated data, it is possible to configure the querying settings to use lazy or eager loading, when working with a persistent store (like a database or web service).

A query that includes associated data, when using lazy loading, will fetch the desired object or collection. Interestingly, the syntax does NOT require you to use the Include() method in the query expression. In the fetch action, the query will not immediately fetch the associated data. Instead, it will wait for an attempt to access (or dereference) the associated object/collection. When that is detected, a second fetch will execute, and return the associated data automatically.

A query that includes associated data, when using eager loading will fetch the desired object or collection, and the associated data. The syntax of the query expression MUST use the Include() method.

Sounds like lazy loading offers a pretty good deal, right? Simpler syntax etc.

Nope. Not for web apps. Do NOT be tempted to use it, as is suggested sometimes by books and web documents.

Why? Interactions with web apps (from a client) follow the HTTP protocol. A request-response cycle is an atomic task, which completes when the response is delivered. The server does not persist request state, in memory, or anywhere else. Therefore, there’s no opportunity for lazy loading to work.

Unfortunately, most of the books and web documents “out there” that include coverage of Entity Framework tends to use lazy loading code. For example, the design model classes use the virtual keyword on navigation properties, which is supposed to trigger the lazy loading functionality. However, it’s ineffective and useless for web apps. Don’t use “virtual”.

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

Advertisements
%d bloggers like this: