CoreCoder
LANGUAGES: C#
ASP.NET VERSIONS: 3.5 | MVC
Model: The M in ASP.NET MVC
Modeling Data for an ASP.NET MVC Application
By Dino
Esposito
According to the original formulation of the MVC
pattern, the model is the object that represents the application gateway to the
business logic. The model holds the current state of the application and replies
to requests for state changes coming from the view via user gestures. The model
also is designed to execute actions requested by the controller, which typically
results in state changes. In a nutshell, the model is the representation of any
data consumed by view and controllers. But what about the MVC pattern as
implemented in ASP.NET MVC?
Well, that s a slightly different thing. In ASP.NET
you find a version of MVC adapted to a new medium the web that just wasn t
around yet at the time in which MVC was first devised (in the 1980s). In terms
of patterns, ASP.NET MVC implements the Model2 pattern rather than the classic
MVC. On the other hand, Model2 was developed as an attempt to bring MVC and its
separation of concerns to the web. Figure 1 shows a UML sequence diagram of how
the Model2 pattern works (and subsequently ASP.NET MVC).
Figure 1:The sequence diagram for the Model2 pattern
What s the Model Really For?
There are a few differences between classic MVC and
Model2. First and foremost, there s no direct contact, let alone an observer
relationship, between view and model. Second, the controller renders the view
and explicitly passes data to it. The user gesture is not captured and handled
by the view as in classic MVC. In a web scenario, the browser captures the
user s activity and posts a request to the web server. On the server side, it
is a system component the front controller that intercepts HTTP requests and
figures out from the URL and headers which controller will be in charge for the
request. From the server perspective, the entry point in the chain is the
controller, not the view as is the case in classic MVC. Today, people often
speak of MVC with regard to the web, but what they actually mean (consciously
or unconsciously) is Model2. And Model2 is exactly the pattern behind
Microsoft s ASP.NET MVC Framework.
So what s the M in ASP.NET MVC? At the end of the
day, the model is any object the controller can talk to that triggers business
actions. Shape and color of the model depends on the architecture of your
back end and the layers you have in place. If you use LINQ to SQL or Entity
Framework to drive data access, you might be led to think that model means an
object model. From a design perspective, this is incorrect. The model is
primarily the gateway to the business logic.
The business logic is then made of business objects
(including services, workflows, and ad hoc components) and an object model. The
object model can be a collection of typed DataSets or, better yet, a LINQ to
SQL or an Entity Framework object model. It also can be something different,
such as a handcrafted hierarchy of plain .NET classes.
If the model is based on typed DataSets, then you
need to write your own objects to manage persistence and perform calculations.
If you opt for LINQ to SQL or Entity Framework, you can kill two birds with a
single stone: You have an object-based representation of the data and a gateway
object to call it the DataContext object in LINQ to SQL and the ObjectContext
object in Entity Framework. In these cases, the underlying framework offers to
manage persistence via its internal O/RM engine. The model , however, also can
be based on a Service Layer pattern and manage persistence via any O/RM in the
marketplace, including NHibernate and Genome. The model also can be
articulated as a set of CSLA.NET business classes, as well as in a variety of
other ways.
The model in modern MVC frameworks, however, is not
simply a library of classes to describe the data being used. It is, instead, a
layer of code that contains data to work with and behavior to be invoked by
controllers.
In the rest of this article I ll focus on the
characteristics of the model that represents the data, assuming you have
another layer that cares about persistence. (This is a common scenario when you
opt for a solution based on LINQ to SQL or Entity Framework.)
Validation in the Data Model
A data model for a layered application has three
main requirements. It must expose a public interface for the data; it must
provide a way to validate any instance of data; and it must publish an API to
work with data and persist it. Persistence depends on the helper framework you
use. Data modeling depends on the approach you take in design. It can be a
database-centric approach, where you start designing your data classes from
some existing database. It also can be a data-centric approach, where you
design your classes in a persistence-agnostic manner and delegate persistence
to a separate data access layer based on some O/RM tools.
What about validation, instead? Figure 2 shows the
structure of a sample class to be used as the root of an object model that
supports validation.
public class MyRootObject : ISupportValidation
{
public virtual bool
IsValid
{
get
{
try
{
return new
ValidationResults().IsValid;
}
catch
{
return false;
}
}
}
ValidationResults ISupportValidation.Validate()
{
ValidationResults
errors =
new ValidationResults();
:
return errors;
}
}
Figure 2:Validation in a data model
The ISupportValidation interface looks like this:
interface ISupportValidation
{
bool IsValid{get;}
ValidationResults
Validate();
}
ValidationResults is the class in your business
logic responsible for reading and applying validation rules. Validation rules
can be defined in a number of ways, and are checked by the business logic. For
example, you can develop your own validation block where you use attributes on
members to define rules. Here s a quick example:
public class Customer : MyRootObject
{
public Customer()
{
:
}
[NotNullConstraint(Message="Customer
ID cannot
be null")]
[LengthConstraint(5, 5,
Message="Customer ID must be
exactly 5 chars
long")]
public virtual string ID
{ get; set; }
:
}
Attributes are part of the validation block and are
checked within the Validate method. Each attribute expresses a business rule.
In the previous example, the Customer ID is set to be non-null and exactly five
characters long. In Microsoft s Enterprise Library 4.1, you ll find similar
attributes and a ValidationResults class as used in this pseudo-code. In
particular, you express business rules in Enterprise Library using attributes
such as NotNullValidator, StringLengthValidator, RegexValidator, and so forth.
The responsibility of validating the state of an
object definitely belongs to the business layer and precisely to objects that
you use to perform operations. In no way does this responsibility belong to the
presentation logic.
Connecting the Validation Layer and Controllers
In ASP.NET MVC, the controller scripts the application s
back end and object model in order to obtain the data it needs to display in a
new or updated view. So it is the controller that validates objects from the
model before proceeding with operations. Here s a sample schema to follow:
// Controller method invoked from the UI
public class ProductController : Controller
{
public ActionResult Insert(Product p)
{
if (p.IsValid())
{
// Add product to
the storage
}
}
}
Any data the controller obtains from the model is
then serialized to the view so a fresh HTML page can be arranged. Likewise, the
controller manages exceptions within the model, including business actions:
// Controller method invoked from the UI
public class ProductController : Controller
{
public ActionResult Insert(Product p)
{
try {
:
} catch {
:
}
}
}
In case of exceptions, it is up to the specific
implementation of the controller to decide whether the native exception from
the model should be managed so that the user interface doesn t even know about
that and degrades gracefully with the empty data it receives. There s a more
general alternative, though. It is based on the assumption that the controller
swallows the original exception and replaces that with another exception that
the view will handle. In ASP.NET MVC, however, the view is a sort of template
processed by a view engine that receives data and fills it in. It s hard to
handle exceptions at the view level. The controller is, ultimately, responsible
for any exceptions raised within the model.
Validation in a LINQ to SQL Scenario
If you ever used LINQ to SQL to create the object
model for your ASP.NET MVC application, you know that the model is created by a
wizard. You certainly can put your hands on the generated file, but at the risk
of losing any changes as you happen to restart the wizard. For this reason,
classes in the LINQ to SQL model are partial. In this way, you can add your
changes to additional partial classes and make them survive future changes made
through the wizard.
One of the key changes you might want to make to
LINQ to SQL classes relates to validation. LINQ to SQL classes are plain .NET
objects with no dependency on the framework. First and foremost, properties of
any classes in the model are annotated with rules inferred from the database
schema. This means, for example, that data types are matched and nullability is
handled properly. Likewise, columns with unique values are honored.
In addition, classes within the model feature a
number of extensibility methods, namely partial methods that may, or may not,
be implemented in some partial class. Many of these methods regard validation.
Here s an example:
[Table(Name="dbo.Customers")]
public partial class Customer : INotifyPropertyChanging,
INotifyPropertyChanged
{
partial void
OnValidate(System.Data.Linq.ChangeAction
action);
partial void
OnCustomerIDChanging(string value);
partial void
OnCustomerIDChanged();
partial void
OnCompanyNameChanging(string value);
partial void OnCompanyNameChanged();
:
}
For each property in the class the wizard defines a
pair of partial methods of the type OnXxxChanging and OnXxxChanged, where Xxx
is the property name. These properties are automatically called from the setter
of each property and provide an effective way for ensuring that no invalid
values are assigned:
public partial class Customer
{
partial void
OnCustomerIDChanging(string value)
{
if
(value.Equals("KOEN2"))
{
throw new
InvalidExpressionException("...");
}
}
}
The OnValidate method, instead, is a placeholder
for you to define when you re looking for just one method to call to ensure
that the object is valid.
Integrating Business Rules
Extensibility methods are not enough. They help you
in preventing situations where code attempts to store invalid values into
properties but don t support you much when it comes to business rules. A
business rule is not simply a syntax rule and does not necessarily apply to
just one property. It is recommended that you further decorate your LINQ to SQL
classes with attributes or code that express and validate business rules. A
possibility is defining a validation interface such as the aforementioned
ISupportValidation and using partial classes to force it in any LINQ to SQL
entity class.
The Validate method will then go through the list
of checks necessary for each entity and report violations to the caller code.
Here s a possible schema for a solution:
public ValidationResults Validate()
{
ValidationResults openPoints
=
new ValidationResults();
if ( ... )
{
string message =
"...";
openPoints.Add( new
Violation( message );
}
:
}
Such a Validate method, or whatever name you want
to give it, will be called from the code before persisting the object or, in
general, at any time in which the validity of the object has to be checked.
Conclusion
Originally formulated to cover the creation of the
entire application, the MVC pattern has been adapted to the web and fully
implemented in the ASP.NET MVC Framework. The model in the pattern represents
the gateway to the back end of the system, as well as any exposed business
actions. At the same time, the model also incorporates the representation of
the data within the application.
ASP.NET MVC doesn t mandate any specific approach
or technology to build the model and add validation to it. So you can use LINQ
to SQL s built-in facilities to implement syntax checks such as nullability and
data types. At the same time, you can use extensibility methods to check
business rules or you can integrate your own business validation layer on top
of LINQ to SQL classes. The business validation layer can be created via
Enterprise Library or it can be your own library. The freedom of implementation
is unmatched; but make sure you don t overlook a layer to validate your MVC
model.
Dino Esposito (dino@idesign.net) is an architect at IDesign,
specializing in ASP.NET, AJAX, and RIA solutions. Dino co-authored Microsoft .NET: Architecting Applications for the Enterprise and Microsoft ASP.NET and AJAX: Architecting Web Applications.