DPS907 notes – Thu Sep 15

Associations – one-to-many, in a web service. Work on Assignment 2.

 

Associations between objects (navigation properties)

Most students in the course are familiar with associations between objects. This section, and the next, will refresh your memory. If you need to learn about associations between objects, continue reading and studying.

Depending upon the situation, we will encounter different kinds of associations:

  • one to many
  • many to many
  • one to one
  • self-referencing one to one
  • self-referencing one to many

One to many will be discussed today, and the others in the next class session.

 

One to many

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

For example:

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

Looking at the other side of the association:

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

 

Refresh your memory – declaring an association

When writing associated classes, ALWAYS add a navigation property to both associated classes.

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

// For almost all one-to-many associations, the to-one end is "required"
[Required]
public Supplier Supplier { get; set; }

 

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

public ICollection<Product> Products { get; set; }

 

In a design model class, the collection type will be an ICollection<T>.

In a resource model class, the collection type will be an IEnumerable<T>.

Remember, whenever you declare a collection property, you MUST initialize it in a default constructor.

 

Refresh your memory – set 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 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.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, and a new 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? No.

 

Refresh your memory – getting 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;

 

AutoMapper usage and tips

Use the AssocationsIntro code example to help you learn the topics in this section.

As you know from past experience, AutoMapper is a convention-based mapper. Between classes, it will map/convert properties that have the same name and type.

It also works nicely with associated entities. The notes below discuss three scenarios:

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

 

Mapping the individual properties of a related object (in a “to-one” association)

If you have an entity that has an associated object – for example, a Course belongs to an academic 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, in CamelCase.

2. Add an AutoMapper map (to the AutoMapperConfig class), which maps the design model class to the new resource model class that you created in step 1 above.

3. In the Manager class method, 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

 

You can see more in the AssociationsIntro code example. Look in the Album_vm.cs source code file.

 

For an object, mapping an associated object 

If you have an entity that has an associated entity – for example, a Course is associated with a single academic 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. Add an AutoMapper map, which maps 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.

 

You can see more in the AssociationsIntro code example. Look in the Album_vm.cs source code file.

 

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. Add an AutoMapper map, which maps 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:

AutoMapperAssociatedCollection

 

You can see more in the AssociationsIntro code example. Look in the Artist_vm.cs source code file, and notice the ArtistWithAlbums class, which includes a IEnumerable<AlbumBase> collection property.

 

Request a resource and include associated resources

There are two typical ways to configure a request for a resource that includes associated resources:

  1. Add a query string parameter to the URI (not recommended)
  2. Use the new 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).

 

More about the sample database in the project template

Recently, you learned about a Visual Studio project template that will help you learn web services. It includes a sample database with hundreds (thousands?) of data rows.

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.

 

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:

chinookmodel2016fall

 

Get started on the assignment

Assignment 2 is now available.

Before leaving the classroom at the end of the timeslot, hand in your work report.

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

Advertisements
%d bloggers like this: