DPS907 notes – week 2 – Sep 14 and Sep 17

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.

 

Practice test today

We will have a brief “practice test” 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 duration of each test depends upon the number of questions. Generally, about 3 to 4 minutes per question is allotted, which seems to work out OK. And, you will have choice – you will be able to answer any “x” of “y” questions.

The tested topics are both forward-looking, and review-based. The forward-looking aspect encourages you to prepare for class, by reading and using the weekly content before the class. You will be tested on comprehension, understanding, and the application of new topics. The review-based aspect enables you to show that you have the foundational knowledge and understanding needed for current and future tasks.

 

Agenda for the Friday class

Here’s the agenda for the Friday class:

  • Q&A about ASP.NET MVC web apps review/reminder topics
  • Features that are new to you, and needed for ASP.NET web services

The in-class coverage and discussion is intended to get you comfortable with the basic or foundational features of a web service project.

You now have some experience with “get all”, “get one”, and “add new” requests. We’ll build on that knowledge.

Next, we’ll learn about, and begin using, a new project template that includes a database with lots of data.

That enables us to set the stage for working with data, associated data, and so on. Each topic builds upon previous topics.

 

Code examples for this week

Remember, code examples are published on a public GitHub repository.

You can use a web browser to view the source code. You can also click the “Download ZIP” button to get a full and complete copy of all code examples in the repository. Code examples will be published weekly, in logically-named folders (e.g. “Week_02”, “Week_03”, etc.).

All HTTP Methods

Debug Intro

Associations Intro

 

Important review / reminder content from your web apps course

In today’s notes, the section titles that begin with “Review / reminder” have content from the web apps course (BTI420 or INT422).

Your professor’s most recent notes from that course are at this link.
That course’s code examples are at this link.

As you know, the knowledge and experience you acquired from that course is a prerequisite for this course. When covering web service topics each week, we will assume that you know, recall, or remember the ASP.NET MVC web apps foundations that you already have.

The “Review / reminder” sections that follow offer both details and a summary of the essential content that you must know before planning and then writing any web service code. Your professor expects that you will review these sections on your own. It will probably take a few hours (at least) to go through the content AND the linked code examples. Do not skip or blow off this work, because you think that you remember everything you learned in the web apps course you took a couple of years ago. We will spend little or no class time on these “Review / reminder” sections, but there will be an opportunity in class to ask questions about them, and you can also contact your professor during his drop-in student help time office hours.

 

Review / reminder: Coding in ASP.NET web apps

Study this content on your own before class.

When you started working on Assignment 1, you probably were reminded about ASP.NET coding practices and principles, and you had to refresh your knowledge and skills somewhat. At this point, we’ll summarize some of the common coding tips, in an effort to increase your productivity and confidence.

Much of the following is from the web apps course’s late January and early February notes.

 

Design model classes

The class name must be singular.

The database-generated unique identifier, and key, should be named “Id”.

Use data annotations. Typically, we can use only Required and StringLength, and only on string properties (with one exception; see below).

Always add a default constructor. Use the “ctor tab tab” practice to easily generate that code block. In the default constructor, initialize the value of:

  • DateTime properties
  • Collections (can usually be of type List<T>)
  • Other property values that you believe must be initialized for safety or data integrity purposes

A navigation property for an associated collection will always be of type ICollection<T>.

A navigation property for an associated object will have the same type as the associated object. Almost always will this navigation property have a Required data annotation. (In other words, it depends upon the existence of an associated object.)

 

Data context

The data context is the “gateway” from your app to your database. The Entity Framework infrastructure provides runtime mapping between your data model (as defined in the design model classes) and database tables.

For each design model class, create a DbSet<T> property. It is then exposed as an entity collection.

 

View (resource) model classes

The properties needed for a resource model class MUST exactly match, or meet the needs of, the use case.

The class name ought to be in the format EntityPurpose, where “Entity” is the name of the entity collection we’re working on (e.g. Customer, Product, etc.), and “Purpose” is the name of the purpose or usage of an object or collection of this type.

Often, for typical data CRUD tasks, we can use a hierarchy of classes. For example:

CustomerAdd – all the properties needed to fulfill an “add new customer” use case

CustomerBase – often inheriting from CustomerAdd, also includes the object identifier property, and maybe other manager or store generated properties

CustomerEditAddress – the object identifier, and the properties needed to fulfill an “edit customer’s address” use case

Do not force inheritance to be used. If it’s right, do it. If not, don’t.

Use data annotations. We can use a wider range of annotations in resource model classes.

Add a default constructor if you have DateTime properties and/or collections (of any kind). And, if you want initial values for any other properties, do that work here too.

Collection types – whether they are collections of simple types, or as navigation properties, must be of type IEnumerable<T>.

 

AutoMapper 

Add the AutoMapper package. (Later versions of the new project template will already have AutoMapper.)

Configure it, as you have learned, in the manager class.

 

Manager methods

The manager defines and creates a reference to the data context class.

Manager method names ought to be in the format EntityTask, where “Entity” is the name of the entity collection we’re working on (e.g. Customer, Product, etc.), and “Task” is the name of the task we’re performing (e.g. GetAll, GetOne, Add, etc.).

Manager methods will accept, as parameters, nothing, or one or more standard types or custom types. Most often, the custom type will be a view (resource) model type.

When working with the data context, we use LINQ to filter and sort the results.

If you need an associated object or collection, the Include() extension method is used.

Manager methods that are used by controllers NEVER accept or deliver design model objects or collections. Instead, they must ALWAYS accept or deliver view model objects or collections.

Manager methods can also return void, or bool, or other types as appropriate for the use case.

 

Controller and methods

Each controller defines and creates a reference to the manager class.

Controller methods NEVER have direct access to the data store infrastructure.

Almost all controller methods will use the IHttpActionResult return type.

Controller method parameter types are simple, or of a view (resource) model type.

Do as little work as possible in the controller method. Rely on the manager methods to do most of the work. However, you can still do your own local filtering and sorting if you wish, before returning the result to the requestor.

 

Filtering

Within a manager method, before returning the results, we often need to perform other tasks on a collection, such as filtering or sorting. For example, assume that we wish to select only those Program objects that have a Credential property value of “Diploma”:

var programs = ds.Programs.Where(c => c.Credential == "Diploma");

 

In the following example, assume that we wish to select only those academic Program objects that include an academic Course object with a specific identifier. Let’s build the statement in two steps.

By default, when we fetch an object that is related/associated with another object and/or collection, the object that is fetched does not include the related/associated objects. That is the default behaviour of the way we have configured the data object.

Therefore, to build the statement, we first must use the Include method, as shown next:

var programs = ds.Programs.Include("Courses");

 

Its return type is DbQuery<TEntity>. (It’s a collection.)

The second task is to add the filtering to the statement, as shown next:

// Assume that "id" has been passed into the method
var programs = ds.Programs.Include("Courses")
    .Where(p => p.Courses.Any(s => s.Id == id));

 

The return type of this statement is IQueryable<TEntity>.

As noted above, return the results as an enumerable. Typically, an AutoMapper mapper does that work.

 

Sorting 

In this example, assume that we wish to sort the Program collection by a property named Code:

var programs = ds.Programs.OrderBy(s => s.Code);

 

The OrderBy() extension method is defined in the Queryable class (look at the inheritance and implementation list above). This method can be used on any object of type IQueryable<TEntity>. Its return type is IOrderedQueryable<T>.

As noted above, return the results AsEnumerable().

 

Filtering and sorting

You can combine tasks by chaining the filtering and sorting tasks above. The statement below has been wrapped for clarity:

var programs = ds.Programs
    .Include("Courses")
    .Where(prog => prog.Courses.Any(crs => crs.Id == id))
    .OrderBy(sort => sort.Code);

 

The Any method returns a boolean, so it’s ideal for use in a lambda expression. It determines whether any element of a sequence satisfies a condition. Therefore it needs a lambda expression of its own.

Here’s how to read, in English, this statement:

Fetch the Program collection…
and for each Program object…
include the related Course object(s)…
but only where a Program object’s Course collection…
contains any Course objects that have an Id property that matches a passed-in “id” value…
then sort the result by the Program object’s Code property value.

As noted above, return the results as an enumerable.

 

Other information

Please – please – take advantage of the existing web apps course content:

petermcintyre.com/bti420/notes

It is (can be, will be) your reference for all the topics for which you need reminders or refreshers. Don’t use unapproved Google search results, use the content linked above instead.

That web site has a search feature. Depending on what you’re looking for, the search feature may not yield very good results.

So, you can use Google to search the web site. For example, to search only the web app course note for “automapper”, enter this in a browser’s address or Google search bar:

automapper site:petermcintyre.com/bti420/notes/

In the list of results, you can ignore the content from past years. The most current results do not include the year number in the URL.

 

Review / reminder: Associated (related) data

Study this content on your own before class.

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 associations. 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;

 

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

Study this content on your own before class.

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; }
}

 

Review / reminder: AutoMapper and associated data

Study this content on your own before class.

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

 

New content is presented in the next sections.

 

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 this section, query techniques are reviewed. Make sure you are, or become, comfortable with query techniques.

Do you need an object, and its associated object or collection?

If yes, then this work must be done in the Manager class.

Use the Include() method, on the DbSet<TEntity> collection. As you will read in the MSDN Reference, the Include() method accepts a string, which is the name of the navigation property for the associated object/collection. (Also as you will read, Include() is actually defined in the DbQuery<TEntity> class. DbSet<TEntity> inherits from DbQuery<TEntity>.)

For example, let’s continue with the objects and collections defined above.

// The following code is in a method in the Manager class

// Get all, each with its associated object
var c = ds.Products.Include("Supplier").OrderBy(p =&amp;gt; p.ProductCode);

// Get one, with its associated collection
var o = ds.Suppliers.Include("Products").SingleOrDefault(s =&amp;gt; s.Id == id);

 

What if you need to include data from more than one associated entity? Simply add another Include() method to the query expression. When you do so, there are some considerations, described next.

 

Include multiple associations, branch pattern:

Study the entity class that you’re starting the query with. Assume that you want to include two (or more) associated entities, and each associated entity has a navigation property. In other words, you’re branching out to include the associated entities.

In that situation, specify multiple Include() methods. For example:

var c = ds.Products.Include("Supplier").Include("Category");

 

Include multiple associations, chain (linked) pattern:

Again, study the entity class that you’re starting the query with. Assume that you want to include two (or more) associated entities (e.g. Foo, Bar), and one has a navigation property (e.g. Foo), but the other (e.g. Bar) is not directly associated with the entity class you’re starting with (but is associated with the direct association). In other words, you’re following a linked path, or a chain of associated entities.

In that situation, in the Include() method parameter, specify a dot-separated path to the final destination, using the design model class property names. For example:

var c = ds.Songs.Include("Album.Artist");

 

Filtering, and associated objects

In the following example, assume that we wish to select only those Program objects that include a Subject object with a specific identifier. Let’s build the statement in two steps.

By default, when we fetch an object that is related/associated with another object and/or collection, the object that is fetched does not include the related/associated objects. That is the default behaviour of the way we have configured the DbContext object.

Therefore, to build the statement, we first must use the Include() method, as shown next:

var c = ds.Programs.Include("Subjects");

Its return type is DbQuery<TEntity>. (It’s a collection.)

The second task is to add the filtering to the statement, as shown next:

var c = ds.Programs.Include("Subjects")
    .Where(p => p.Credential == "Degree");

The range variable – p – represents an object in the Programs collection. Its type is Program.

The return type of this statement is IQueryable<TEntity>.

 

Filtering and sorting, and associated objects

You can combine tasks by chaining the filtering and sorting tasks above (using the “fluent” syntax). The statement below has been wrapped for clarity:

var c = ds.Programs
    .Include("Subjects")
    .Where(p => p.Credential == "Degree")
    .OrderBy(p => p.Code);

Here’s how to read, in English, this statement:

Fetch the Program collection…
and for each Program object…
include the related Subject object(s)…
but only where a Program object’s Credential property value…
matches the string “Degree” …
then sort the result by the Program object’s Code property value.

 

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”.

 

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

 

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 an instance of 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. In general, for our data-oriented requests, JSON will be returned.

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.

Later in the course, we look at content negotiation in more depth.

 

Supporting all relevant HTTP methods

This content will be split over two weeks, this week and next. This week, we will focus on the easier group of HTTP methods, GET and POST. You will need this coverage to complete the assignment. Next week, we will cover PUT and DELETE.

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 (“Exception handling, version 1”), you will learn that our manager methods will always 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 last week’s 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 all” explained

In the manager, “get all” will fetch all objects in a collection. There may be zero objects, or more. From the manager method, a collection will be returned, which could be empty, or it could have one or more objects.

In the controller, call the manager method.

The return value will never be null, so return an Ok() result, along with the collection.

 

“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}";

 

Next week, we’ll cover “update existing” and “delete item” in detail.

 

(End of Friday content; Monday content is next.)

 

Agenda for the Monday class

Here’s the agenda for the Monday class:

  • Debugging
  • Project template

 

Debugging ASP.NET projects

Many students will be familiar with the Visual Studio debug tools as a result of their work in C++, and the debug process in ASP.NET projects. If you had your web apps course in the Winter 2017 academic term, then you learned how to debug ASP.NET MVC web apps in early February.

In the computer-lab session on February 3 or 6, you had an in-class guided exercise on debugging. Skim those notes to refresh your memory and remind you of the process.

Note: If you’re not comfortable with the debug process, then stop now, and carefully go through those notes, and the code example. The time you spend getting comfortable debugging will be returned to you many times over in the future. Debugging is an essential and important skill.

 

In this week’s code example repository, there is a DebugIntro project. Get it, and run it in debug mode. Interact with it using Fiddler. In the past, you used a browser to send a request. Now, you use Fiddler.

As delivered, it will fail with “get all customers” request.

Also, as delivered, it will fail with the “edit customer” request.

 

Exception handling, version 1

This is an initial entry-level treatment of exception-handling. Later in the course, we will learn more about this topic, and improve our treatment.

The recent code examples will help you learn how to handle errors and exceptions in your web service.

 

HTTP status codes for errors

In our web APIs, if there is an error, we must return an HTTP error code.

Study the list of HTTP status codes. We may use a number of ‘client’ error codes, and maybe a few ‘server’ error codes:

Code Title Description
400 Bad request Request syntax is not correct
401 Unauthorized Authentication required; the response includes how-to-authenticate info
403 Forbidden Authentication was successful, but the request fails authorization
404 Not found
405 Method not allowed Method not allowed for the resource; response MUST include an Allow header containing a list of valid methods for the requested resource
415 Unsupported media type Request entity body format is not supported
500 Internal server error Default generic message, please limit its use in your code
501 Not implemented Request method not implemented; cannot fulfill the request

Looking for documentation? This Wikipedia article is readable. However, the official source is RFC 7231, in Section 6.

 

Coding principles

Our “version 1” strategy is to handle request errors in a simple yet informative manner.

In this version, our manager methods will return data, or null. The null value will indicate that there’s no data, or an error.

In our controller methods, we will interpret null as ‘no data’ or an error. Its meaning will depend on the method’s context.

Later, our “version 2” strategy will be extended to handle additional kinds of errors, in a way that covers the entire application, with less code to write and maintain.

 

Introducing a new project template

Recently, you learned that we plan to use custom-configured Visual Studio new project template that will help you learn web services. It includes a sample database with hundreds (thousands?) of data rows. (You may have used custom new project templates in your web apps course too.)

The sample database holds data for a music business. It has artists, albums and tracks. It has customers, who have purchased music, so there are invoices and invoice details.

The new project template is in the “Templates_and_solutions” folder in the repository. Here’s how to use it:

  1. Download (or clone) the repository.
  2. Open the Templates_and_solutions folder, and select the file named “Web service project v1.zip”. (Do NOT unzip the file.)
  3. Copy it to this folder on your system:
    %userprofile% > Documents > Visual Studio 2017 > Templates > Project Templates > Visual C# > Web

Then, when you create a new project in Visual Studio, it will be available for selection.

 

Reminder about the identifier

Almost all entity classes have an identifier property, usually an integer. Its name is almost always “Id” or “<entity>Id”.

For example, a Product class could have an identifier named “Id”, or “ProductId”.

For new entity classes that we write, we recommend using the simple “Id” name. In our opinion, including the name of the entity class in the identity-property name is redundant; it adds no value.

The sample database does not follow that recommendation. (We did not create the sample database. Luis Rocha probably did.) Instead, its design model class identifier properties have names like this:

  • ArtistId
  • AlbumId
  • TrackId

Problem? Yes, in resource model classes, when working with the Visual Studio web app view generator/scaffolder, and the Web API framework JSON and XML media formatters (i.e. the serializers). Let us explain:

A resource model class for the Artist entity might be named “ArtistBase”. The generator / scaffolder / media formatter will assume that the identifier is “ArtistBaseId” – in other words, the class name, plus the “Id” suffix. However, the identifier is simply “ArtistId” (without the word “Base” inside).

How can we fix this?
Use the [Key] data annotation, in resource model classes, for identifier properties.

 

Class diagram

Here is a class diagram, in PNG image format. Click to open it full size in a new tab/window:

chinookmodel2016fall

 

Work on the assignment

Begin work on Assignment 2.

 

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

Advertisements
%d bloggers like this: