DPS907 notes – Tue Sep 23

Working with associated objects in a web service. Introduction to the repository and unit-of-work patterns.

.

Quiz today

Today’s session begins with a quiz.

.

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

You will often model real-life objects that have associations with other objects, in a one-to-many relationship. (In the near future, we will work with the other kinds of associations.)

For example:

  • a ‘Program’ has a collection of ‘Subject’ 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 ‘Subject’ object belongs to a ‘Program’, and similarly,
  • a ‘Product’ is made by a ‘Supplier’, and
  • a ‘Vehicle’ is made by a ‘Manufacturer’.

When performing data operations, you can work with either side of the association:

  • When working with a to-one navigation property, you simply assign an object to the property (e.g. blah.Supplier = s;)
  • When working with a collection navigation property, you add a new item with the collection’s Add() method (e.g. blah.Subjects.Add(s);)

.

AutoMapper tips

Including an associated (related) collection

If you have an entity that has an associated (related) collection – for example, a Program has a collection of Subjects, 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 three things:

1. In a new resource model class, add an appropriate navigation property for the collection. The property names must match. (And you will need AutoMapper maps defined in Global.asax.cs, but you probably already have them done.)

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

3. In the controller, add a method that will call the manager/repository method; a typical pattern is to use a query string parameter.

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

AutoMapperAssociatedCollection

.

Including a property from an associated object

If you have an entity that has an associated object – for example, a Subject belongs to a Program, or a Product belongs to a Supplier – you may want to return an object with its associated object.

To do this, you need to do three things:

1. In a new resource model class, add an appropriate navigation property for the associated object. The property names must be a composite of the class name and the property name. (And you will need AutoMapper maps defined in Global.asax.cs, but you probably already have them done.)

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

3. In the controller, add a method that will call the manager/repository method; a typical pattern is to use a query string parameter.

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

AutoMapperComposite

.

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 (demonstrated below)
  2. Use the new attribute routing feature (covered in a few weeks)

.

Using patterns in your work

Think about your ‘manager’ class. It has data service methods for fetch, add, change, and delete. For each entity.

In the near future, you will be working with many entities. What would the ‘manager’ class look like with methods that handle a dozen or so entities? Lengthy, and filled with repeated code.

Is there a better way?

Yes. We can use a ‘repository’ pattern.

Please note that there is no one pure way to use the repository pattern in our type of project (ASP.NET Web API). This semester’s treatment is new. (Last time the course ran, we did use a lengthy and repetitious ‘manager’ class.)

A warning: The ‘generic repository’ topic is widely discussed on the web. Your professor has yet to find a treatment that meets our objective (which is to create a modern web service that is safe, reliable, scalable, and adaptable). The biggest problem is that all ‘generic repository’ examples out there assume that you are always working with domain (i.e. design model) objects. We don’t want to do that. We require the controllers to work with resource model objects, so their interface to the repository (via repository methods) must read and write resource model objects. So don’t waste time looking for solutions ‘out there’. Work with THIS information. If you believe that the information is not enough for you to complete your work, contact your professor (and as a result, this information may be edited).

Using the repository pattern yields one more future benefit – testability. If we have time in this course, we will study this topic.

.

Components (or ‘moving parts’) 

We continue to use the following:

  • Design model classes
  • Resource model classes
  • AutoMapper
  • Data context class, store initializer, and migrations
  • Controllers

We change the way we work with the ‘manager’ class. Starting now, the role of the ‘manager’ class will be contain business or application logic. We will not use it much for typical data access operations.

What ‘new’ components are involved?

  • “Repository” base class
  • Customized repository class for each entity (or entity group), which uses resource model objects and collections
  • “Unit of work” class that collects and coordinates access to all the repositories

.

Repository base class

Study the code example named PatternsIntro. Look at the Repository.cs source code file, and notice its declaration:


public abstract class Repository<T> where T : class

The repository base class is marked as abstract. This simply means that you cannot create an instance of the class, but you can inherit from it in a derived class.

Note that his abstract base class (ABC) includes a type parameter (T), which is specified/named in the derived class’ declaration.

This ABC includes variables to hold references to:

  • the data context
  • the DbSet for the entity specified in the derived class

One of the goals of this ABC is to provide method declarations for common data operations:

  • get all
  • get all, filtered by some matching condition
  • get one by its identifier
  • add new
  • edit existing
  • delete existing

All methods except “delete existing” are marked as protected. This means that they cannot be called from outside the derived class. In other words, a controller will not be able to directly call these methods. These five methods begin with the upper-case letter “R”. For example:


protected IEnumerable<T> RGetAll()

OK… so how does a controller call a ‘get all’ or ‘add new’ method? The solution is to:

  • write a derived class that inherits from this ABC, and
  • write custom public methods in the derived class.

We’ll do that next.

.

Derived class for an entity (which inherits from the generic repository class)

In the code example named PatternsIntro, look at the Manufacturer_repo.cs source code file, and notice its declaration:


public class Manufacturer_repo : Repository<Manufacturer>

By inheriting from the ABC, and specifying the entity type, it means that this repository will look after data operations for this specific entity. You MUST write the following:

  • a default constructor, which specifies the data context
  • custom public methods (to support data operations)

The work that the custom public methods do, as you will see, is mostly object mapping. As noted above, a repository’s public interfaces MUST be defined by using resource model types (and can also include .NET Framework types and void). DO NOT leak details of the domain (i.e. design model) classes outside the repository.

Study the methods. Notice they often accept a resource model argument, and return a resource model object or a collection. Also notice that they often call a method (which begins with the upper-case letter “R”) that was defined in the ABC.

Please note that you do NOT have to use the “R” methods. You are NOT forced to use them.

If you need custom functionality, simply write your own method. Your code has direct access to the _dbset variable, so you can perform data operations directly on the data store.

.

Unit-of-work class

The purpose of this class will be to act as a ‘gateway’ to the app’s data model.

A controller will have a reference to an instance of this unit-of-work class. This class has references to each entity collection in the data context. Nice and centralized.

In the code example named PatternsIntro, look at the Worker.cs source code file.

Most of the code manages the lifetime of the class. However, as you see, you must add this kind of code to the class:

  • private variables which act as backing stores for the repository properties
  • custom properties with getters only (no setters)

.

Using the repository and unit-of-work pattern

In your past work, each controller declared a reference to a ‘manager’ class.

Simply replace that with a reference to this unit-of-work class. That’s all you have to do.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: