DPS907 notes – Fri Sep 13

Repositories. AutoMapper. LINQ. Lambdas.

.

New way (for your professor) to post code examples

Code examples for this course will be posted on GitHub.

The link in the upper-right area of this page (DPS907 Web service dev links) will open the repository.

We’ll spend a few minutes at the beginning of the class/lab session exploring.

.

Introduction to “view model” classes

In the last class session, we discussed view model classes.

From the list below, we covered questions 1 and 2. We’ll continue today, with answers to questions 3 through 6:

  1. What are view model classes? Why have them?
  2. How to create view model classes
  3. Redesign the “DataService” class to become “repository” classes
  4. Patterns for using the new view model classes and repositories
  5. What is AutoMapper? What problem does it solve?
  6. Configuring maps and using them in repositories

.

Go back to the notes from the last session, and read about best-practice suggestions for question 2, ‘How to create view model classes’.

.

Redesign the “DataService” class to become “repository” classes

You can predict how a “DataService” class can grow in size, to include tens of methods. In a complex app, it’s possible that a “DataService” class can have hundreds of methods.

With this new view model classes topic, we have an opportunity to refactor the data service class idea, which will make future development easier. A data service (or manager) class will include only members that implement business or application logic, and new repository classes will include members which deal with data access. To re-state:

  • Data service (manager) class – business or application logic
  • Repository classes – data access operations

In the ViewModels folder, create a new source code file named RepositoryBase.cs. It will have a constructor and some configuration statements. Study the class in the first code example for today.

Then, for each app domain model class, or logical grouping of classes, create a new source code file, named Repo_<ClassName>.cs. (For example, Repo_Supplier.cs.) Its class will inherit from RepositoryBase, and include the same kind of members that you would typically see in a “DataService” class.

There’s one big difference, however. A repository class MUST accept or deliver only view model objects (or collections). A repository class MUST NOT “leak” anything about the app domain model classes to the controllers and views.

Therefore, the arguments passed to repository methods MUST be simple value types or view model objects. Their return values MUST also be simple value types or view model objects (individual objects, or collections of objects).

.

Patterns for using the new view model classes and repositories

For incoming data, the repository will accept view model objects (or simple scalar/value types). If it needs to, it will transform the data in the model object into an instance of an app domain model class, by mapping / assigning property values.

Then, it can perform typical operations, as you know from a “DataService” class.

For return data, the repository will deliver view model objects. If it needs to, it will transform the data in the app domain model class into an instance of a view model class.

Study the code example to see the patterns for get one, get all, add, update, and delete.

.

What is AutoMapper? What problem does it solve?

If you studied the code, you saw many lines of code that simply mapped / assigned property values between objects. When mapping between objects that have many properties (20+ for example), you’ll have at least a line of code for every property.

That’s tedious. Boring. Error-prone. Ugly.  Awful. Avoidable.

AutoMapper, by Jimmy Bogard, helps solve this problem. (Here’s his blog post that introduced AutoMapper.)

It is a convention-based mapper. When it maps between classes, it simply maps / assigns values if the property types and property names match. Easy. Non-matching properties are ignored. (There are loads of other features, and you can learn some of then later.)

How do you add AutoMapper to your app?

Right-click your project, and choose “Manage NuGet Packages”. A dialog appears. In the left-side navigator, click/select “Online”. In the upper-right “search” area, enter (and look for) “automapper”.

The search results appear in a list in the middle panel. Click/select the AutoMapper item, and click the “Install” button. This action will copy the AutoMapper library to your “packages” folder, and update your project’s “packages.config” source code file.

Now it’s ready to use anywhere, simply by adding a “using AutoMapper;” directive to any source code class file.

.

Configuring maps and using them in repositories

The best practice way to configure and use AutoMapper is described next.

In Global.asax.cs, add statements that will “configure” maps. This action ensures that a map is available to any of your classes. For example:

  • Mapper.Create< source_type , target_type >();

Add these “configure” statements for every map that you think that you’ll need. Group them by app domain model class. You will need one or more:

  • Maps “to” view model classes (from app domain model classes)
  • Maps “from” view model classes (to app domain model classes)

.

For each use case (that you identified in a previous section), create a map, from an app domain model class, to the custom-and-specific view model class that will service that use case.

Typically, you will have maps that “shape” the delivery of data to the view.

Also, you will have maps that enable a view to “add” or “update” items.

After the maps have been defined you can use the Mapper.Map<T>() method anywhere you need to. Sources/targets can be one-ofs, or collections. One statement replaces several. What a deal. For example:

  • Mapper.Map< target_type >( source_type );

.

Working with your classes in an Entity Framework environment

As a reminder/refresher, the following information will help you when working with your object classes in EF code first.

You need to be aware of the following:

  • The Entity Framework infrastructure provides runtime mapping between your data model classes and entity classes
  • An object (an instance of a model class) can be used in methods that need a TEntity object
  • Collections of objects are typically not generic lists; see below for more

.

The data context is the gateway from your app to your database. Its data type is DbContext. It is the object that your app’s code uses when working with the persistent store.

As you know, you must create a class that inherits from DbContext. In that class, you write a constructor, and include properties for each data model class collection. The data type of the properties is DbSet<TEntity>, where TEntity is the name of one of your model classes.

DbSet<TEntity>…

  • inherits from DbQuery<TEntity>
  • implements IDbSet<TEntity>
  • implements IQueryable<TEntity>
  • implements IEnumerable<TEntity>
  • implements IQueryable
  • implements IEnumerable

.

In the data service / manager (or repository) class, you typically define and create an instance of the data context:

private DataContext ds = new DataContext(); // ds simply means “data store”

Assume that our app domain data model covers administrative objects in the School of ICT. For example Program, Subject, Employee, Semester, Course.

If you want to work with the Program collection, you use a statement like this:

var programs = ds.Programs;

As noted earlier, the data type of programs is DbSet<TEntity>, specifically DbSet<Program>. When you are ready to return the collection, you MUST transform it into a generic collection. You MUST NOT leak Entity Framework types to controller classes. That’s why we do this:

return programs.AsEnumerable();

.

Filtering

Within a manager/repository 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 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 programs = 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:

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

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

As noted above, return the results AsEnumerable().

.

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(“Subjects”)
.Where(prog => prog.Subjects.Any(subj => subj.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 Subject object(s)…
but only where a Program object’s Subject collection…
contains any Subject 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 AsEnumerable().

.

.

.

.

.

.

.

.

.

  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: