DPS907 notes – Mon Sep 28

Refactoring to improve your productivity.

.

Using patterns in your work

Think about your “manager” class. It has data service methods for fetch, add, change, and delete. For each entity. And, it may have other methods, for application (or business/domain) logic.

In your Lab 2 manager class, you may have had about 100 lines of code, to support the fairly simple data service tasks for one entity.

When you studied last week’s AssociationsOther code example, you may have noticed that its manager class had about 450 lines of code, to support the data service tasks for three entities.

In the near future, you will be working with more entities. What would the manager class look like with methods that handle a dozen or so entities? Lengthy (1000 lines of code? 2000?), 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 is only the second time that we have used this implementation in the course. (Before that, we did use a lengthy manager class.)

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

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 design model objects in your controllers. 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.

Therefore, don’t waste time looking for solutions out there (by using a search engine). Work with THIS information. If you believe that the information is not enough for you to complete your work, contact your professor for help (and as a result, this information may be edited).

.

Components of the solution

We will continue to use the following:

  • Design model classes
  • Resource model classes
  • AutoMapper
  • Data context class, and related facade infrastructure
  • Controllers

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

Which 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

.

Getting started

Create a new folder in the root of your project, named “ServiceLayer”.

It will hold these source code files:

  • Repository base class, and derived entity (group) repository classes
  • Worker class
  • Manager class

.

Repository base class

Study the code example named RepositoryIntro. (You will use that as a base for your Lab 3 work.)

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 RepositoryIntro, look at the Artist_repo.cs source code file, and notice its declaration:

public class Artist_repo : Repository<Artist>

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:

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 application/business domain (i.e. the design model) classes outside the repository.

Study the methods.

Notice their names. They do not have to include the name of the entity, because the calling path syntax (in a controller) includes an entity name.

Then, 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 unit-of-work class has references to each entity collection in the data context. Nice and centralized.

In the code example named RepositoryIntro, 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 for each repository, with getters only (no setters)

.

Manager class

In a simple project, the manager class may be almost empty. As you add custom business or application logic, you will add more members to the manager class.

It starts with a private field, to hold a reference to the unit-of-work class.

Its constructor accepts a reference to the unit-of-work class, and assigns it to the private field.

From that point on, code can access the app’s data model, as it performs its custom business or application logic.

.

Using the repository and unit-of-work pattern

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

Simply replace that with references to the unit-of-work  and manager classes. Add a default constructor to do this work.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

%d bloggers like this: