Ian Tipson

Professor – School of ICT – Seneca College – Toronto, Canada

Data Annotations and ViewModels

with one comment

Data annotations

Data annotations are C# code attributes that you can add to your classes and properties.

Like any attribute, you can add a data annotation on the line before a class or property declaration. The sytnax of an attribute is a keyword, with an optional expression, surrounded with square brackets. Several examples are shown below.

We will use data annotations for two main purposes:

  1. Now, we can add then to our app domain model classes, and they will help us better describe and configure the backing store (i.e. the database)
  2. In the near future, we will add them to “view model” classes, which will enable input data validation in an easy and predictable manner

.

The useful data annotations are mostly from the System.ComponentModel.DataAnnotations namespace. However, we will use a few others, from other namespaces. (Add a “using” directive to bring the namespace into scope.)

The following is a list of typical data annotations that we can use on our app domain model classes now. We will discuss their effects in class (and you can look at the reference material online):

System.ComponentModel.DataAnnotations

[Required]

[MaxLength(n)]

[MinLength(n)] – this annotation is used by the Entity Framework infrastructure for validation – it is NOT used in the database table column definition

[Key] [Key, Column(Order=0)]

[NotMapped] – typically used for a property that generates its value with a getter (and the value is NOT stored); e.g. FullName, a string concatenation of FirstName and LastName, with a space between

[DatabaseGenerated(DatabaseGenerationOption.Computed | Identity | None)] – can use Identity for an int key column that needs an auto-generated incrementing value; can use Computed for a DateTime column

Others: Timestamp

.

The following is a list of typical data annotations that we can use on our “view model” classes soon. We will discuss their effects in class (and you can look at the reference material online):

[ScaffoldColumn(false)]

[ReadOnly(true)]

[DataType(DataType.Password | Currency | Date | Time | DateTime | MultilineText | PhoneNumber | EmailAddress | URL)]

Others: DisplayFormat, UIHint, HiddenInput.

[StringLength(n)] – maximum length

[StringLength(n, MinimumLength=n)]

[RegularExpression(@"regex")]

[Range(min, max)] – int or double

[AllowHtml]

[Display(Name="new desired display name of the property")] Can add Order=<integer> to this attribute to control its order in the UI when you’re using EditorForModel.

System.Web.Mvc

[Compare("nameOfTheCompareToProperty")] Note: Can add another parameter to the attribute – ErrorMessage=”text”.

You can also create your own custom annotation. See the textbook, page 129.

.

Introduction to “view model” classes

We need to talk about view model classes. They take on the exact shape of the data you wish to work with in a view. The view model provides the model data needed by a View in order to display the User Interface (UI). The view model reacts to events and may update data in the model in response to an event. The view model is the bridge between the View and the Model.

This is an important topic.

(You must attend all lectures, because this topic’s details will be delivered verbally. It is unlikely that these notes will be fully updated before the lectures.)

Here’s the questions that we’ll ask and answer:

  1. What are view model classes? Why have them?
  2. How to create view model classes
  3. Redesign the “Manager” 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

.

What are view model classes? Why have them?

As noted above, a view model class takes on the exact shape of the data you wish to work with in a view.

The properties in a view model class are based on the properties in an app domain model class.

We now have a goal to prevent app domain model classes from being visible and accessible in controllers and views. Instead, in controllers and views, we will use view model classes. This implies that we must map data between classes.

This will help to implement the “separation of concerns” principle.

.

How to create view model classes

Create a new folder, named “ViewModels”.

Create a new source code file for a class. We suggest that you create a separate source code file to hold the view model classes needed for each controller. For example, this week’s code examples use a “Suppliers and Products” application domain. Therefore, you should create a source code file named “VM_Supplier.cs”, and another named “VM_Product.cs”.

What classes do you write? The answer is dependent upon your app’s use cases.

Study the first code example, and notice the classes in each source code file.

.

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

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

With this new view model classes topic, we have an opportunity to refactor the Manager class idea, which will make future development easier.

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.

Then, for each app domain model class, or logical grouping of classes, create a new source code file, named Repo<ClassName>.cs. (For example, RepoSupplier.cs.) Its class will inherit from RepositoryBase, and include the same kind of members that you would typically see in a “Manager” 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. 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 “Manager” 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 form 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.

That’s tedious. Boring. Error-prone. Ugly. Whatever.

AutoMapper, by Jimmy Bogard, helps with this task. (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 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.

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. One statement replaces several. What a deal.

.

(The following notes are a work-in-progress. They will be updated, and/or moved to future class notes pages.)

.

Passing data from the controller to the view, again

Pass the model in the return View(model); statement.

In the view, @model T. Alternatively, can add to Web.config in the Views folder. However, avoid doing that for now.

Then, it’s accessible in the Model property.

We need to talk about view models. They take on the exact shape of the data you wish to work with in the view.

What if the model object does not include all of the data that a view needs? For example, in addition to model data, you need select list data. Two solutions:

  1. Pass the additional data in ViewBag
  2. Create a custom view model object, configure it in the controller method, then pass it to the view (where the components will be extracted and used)

This SO post introduces the which-is-better argument. The textbook also has coverage on page 88.

FYI – In a view’s form, you may want to use an HTML Helper, and you want it to include another attribute. From the textbook page 101, nearly every helper includes an htmlAttributes parameter in an overload. It accepts an anonymous object (e.g. new { } ). If an attribute is a reserved C# keyword (e.g. “class”), precede it with an at sign ( @ ). For example:

@Html.Blah(“Foo”, new { target=”_blank”, @class=”editForm” })

.

Input data validation introduction

Html.ValidationSummary

Html.ValidationMessageFor (and …ForModel)

Html.EditorFor – single property.

Html.EditorForModel – model (or view model) object.

.

Data store, database migrations, Azure

Julie Lerman’s article, from February 2012 (no Azure)

MSDN Data Developer docs – Get Started with Code First Migrations

How-to combine EF, code first, database migrations, Azure This is an Azure document

Some dude’s take on the same topic – here

.

Scaffolding

(tba)

Rick Anderson “Using the drop-down list” example…

The StoreManager > EditVM view uses an Html.HiddenFor helper.

The view’s form posts back to StoreManager > Edit, with model binding.

Is that how it keeps track?

It appears that the Html.BeginForm constructor accepts the “Edit” and “StoreManager” arguments, but then constructs a URL fragment that includes the {id}, e.g.: /StoreManager/Edit/4

Looks like it retains the {id} route value of the current URL.

.

More about EF CF

Convention – primary key:

  • If the property is named id, or <className>id, then it will be a primary key (upper/lower/mixed case does not matter)
  • If the property is an int type, then it will be an identity column, with a database-generated value, starting at 1, incrementing by 1

Composite primary key:

  • Add this attribute before every property in the composite primary key, replacing “n” with 0, 1, 2, etc. – [Key, Column(Order=n)]

Resources:

Julie Lerman – Code First DataAnnotations

Rowan Miller – Data Annotations in the Entity Framework and Code First

Rowan Miller – Conventions for Code First

MSDN – http://msdn.microsoft.com/en-us/library/ff798384.aspx

SO posts – here and here

Morteza Manavi – blog with posts on Entity Framework

©Ian Tipson

Written by iantipson

March 3, 2013 at 5:29 pm

Posted in ASP.NET - MVC 4

One Response

Subscribe to comments with RSS.

  1. Pretty! This was a really wonderful article.

    Thank you for supplying this information.

    Karla

    July 13, 2013 at 4:00 pm


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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: