Вы находитесь на странице: 1из 16

View Model pattern and AutoMapper in

ASP.NET MVC Applications


In real world ASP.NET MVC applications, we have to use model objects specially designed for our
ASP.NET MVC views. Domain objects are designed for the needs for domain model and it is
representing the domain of our applications. On the other hand, View Model objects designed for our
needs for views.
The below is the domain model of our demo

We have a Contact domain entity that has a association with Contact Group. While we creating a
new Contact, we have to specify that in which contactgroup belongs with the new contact.

The below is the user interface for creating a new contact


The below is the user interface for creating a new contact

The above new contact UI has the form fields for representing the Contact entity and a GroupId field
for representing the contact group.
The below is the class for Contact View Model

public class ContactViewModel


{
public int Id
{
get;
set;
}
[Required(ErrorMessage = "FirstName Required")]
[StringLength(25, ErrorMessage = "Must be less than 25 characters")]
public string FirstName
{
get;
set;
}
[Required(ErrorMessage = "LasttName Required")]

[StringLength(25, ErrorMessage = "Must be less than 25 characters")]


public string LastName
{
get;
set;
}
[Required(ErrorMessage = "Email Required")]
[RegularExpression("^([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zAZ]\\.)+[a-zA-Z]{2,9})$", ErrorMessage = "Not a valid email")]
public string EMail
{
get;
set;
}
[Required(ErrorMessage = "Phone Required")]
public string Phone
{
get;
set;
}
public string Address
{
get;
set;
}
[Required(ErrorMessage = "GroupId Required")]
public int GroupId
{
get;
set;
}
public IEnumerable<SelectListItem> ContactGroup
{
get;
set;
}
}
Our Contact View Model is designed for the purpose of View template and contains the all validation
rules. It has properties for mapping values to Contact entity and a GroupId property for taking Id
value from ContactGroup drop-down. And also providing a property ContactGroup for binding values
to ContactGroup drop-down.
The below code is Action method for Create Contact HttpGet request where we are creating an
instance of Contact View Model and sets the Contactgroup property for binding values to drop-down
list.

/HttpGet for the Create Contact


public ActionResult Create() {
var viewModel = new ContactViewModel();
var groups = contactRepository.ListGroups();
viewModel.ContactGroup = groups.ToSelectListItems(-1);
if (groups.Count<ContactGroup>() == 0)
return RedirectToAction("Index", "Group");
return View("Create", viewModel);
}
The below code is Action method for Create Contact HttpPost request.

[HttpPost]
public ActionResult Create(ContactViewModel contactToCreate) {
if (ModelState.IsValid) {
Contact newContact = new Contact();
AutoMapper.Mapper.Map(contactToCreate, newContact);
contactRepository.CreateContact(contactToCreate.GroupId, newContact);
}
}
In the above action method, our model binder is the ContactViewModel and we have to map values
of ContactViewModel object to Domain model object Contact for the persistance purpose. In
ASP.NET MVC applications, you have to map values between view model objects and domain model
objects in many situations. In such scenarios, you can use AutoMapper for mapping values between
objects to objects.In our code we copy values from our View model object ContactViewModel to
domain model object Contact. AutoMapper is a convention based object to object mapper framework
developed by Jimmy Bogard.
A mapping configuration is a one time task and the below code setting map configuarion between
our ContactViewModel and Contact.

Mapper.CreateMap<ContactViewModel,Contact>();

The below code from the Create method of HttpPost action, copy values of ContactViewModel object
to Contact object

AutoMapper.Mapper.Map(contactToCreate, newContact);

Check the Flattening for mapping to take a complex object model and flatten it to a simpler model.
AutoMapper is available athttp://automapper.codeplex.com/ and the source code is hosted
at http://code.google.com/p/automapperhome.

How to Use ViewModel with ASP.NET MVC

What is a Model

Parts of the application that implement the domain logic.

also known as business logic.

Domain logic handles the data that is passed between the database and the UI.

As an example, in an inventory system, the model keeps track of the items in storage
and the logic to determine whether an item is in stock.

In addition, the model would be the part of the application that updates the
database,when an item is sold and shipped out of the warehouse.

Often, the model also stores and retrieves model state in a database.

What is a ViewModel

Allow you to shape multiple entities from one or more data models or sources into a
single object.
Optimized for consumption and rendering by the view.
It shows below image

Why We Use ViewModel


1. If you need to pass more than one thing to a strongly-typed view (which is best practice),
you will generally want to create a separate class to do so.

2. This allows you to validate your ViewModel differently than your domain model for
attribute-based validation scenarios

3. Can be used to help shape and format data.


e.g: need a date or money value formatted a particular way?
ViewModel is the best place to do it.

4. The use of a ViewModel can make the interaction between model and view more simple

ViewModel Interaction with Model and View

Where Should We Create ViewModel


(physically)

1. In a folder called ViewModels that resides in the root of the project. (small applications)

2. As a .dll referenced from the MVC project (any size applications)

3. In a separate project(s) as a service layer, for large applications that generate


view/content specific data. (enterprise applications)

Best Practices When Using ViewModels


1. Put only data that you'll render (use in View) in the ViewModel

2. The View should direct the properties of the ViewModel, this way it fits better for
rendering
and maintenance

3. Use a Mapper when ViewModels become Complex (i.e. How to Use ValueInjecter ? )

Let's Try with Simple Example

Asp.net MVC 5,C# and Visual Studio 2013 express has been used.

Please follow an In-line Comments on Code.

This is a simple application with product category drop down list, product name text
box and Save button.

Sample Project's Solution Tree

Domain Models
Product.cs
public class Product
{

public Product() { Id = Guid.NewGuid(); }


public Guid Id { get; set; }
public string ProductName { get; set; }

public virtual ProductCategory ProductCategory { get; set; }

ProductCategory.cs
public class ProductCategory
{
public int Id { get; set; }
public string CategoryName { get; set; }

public virtual ICollection<Product> Products { get; set; }


}

ViewModel
ProductViewModel.cs

public class ProductViewModel


{
public Guid Id { get; set; }

[Required(ErrorMessage = "required")]
public string ProductName { get; set; }

public int SelectedValue { get; set; }

public virtual ProductCategory ProductCategory { get; set; }

[DisplayName("Product Category")]
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}

Controller Action Methods


/// <summary>
/// to generate view with categories for entering product data
/// </summary>
[HttpGet]
public ActionResult AddProduct()
{
//instantiate the product repository
var productRepository = new ProductRepository();

//for get product categories from database


var prodcutCategories = productRepository.GetAllProductCategories();

//for initialize viewmodel


var productViewModel = new ProductViewModel();

//assign values for viewmodel


productViewModel.ProductCategories = prodcutCategories;

//send viewmodel into UI (View)


return View("AddProduct", productViewModel);
}

/// <summary>
/// to save entered data
/// </summary>
[HttpPost]
public ActionResult AddProduct(ProductViewModel productViewModel) //save entered data
{
//instantiate the product repository
var productRepository = new ProductRepository();

//get product category for selected drop down list value


var prodcutCategory=productRepository.GetProductCategory(productViewModel.SelectedVal

ue);

//for get all product categories


var prodcutCategories = productRepository.GetAllProductCategories();

//for fill the drop down list when validation fails


productViewModel.ProductCategories = prodcutCategories;

//for initialize Product domain model


var productObj = new Product

{
ProductName = productViewModel.ProductName,
ProductCategory = prodcutCategory,
};

if (ModelState.IsValid) //check for any validation errors


{
//code to save valid data into database

return RedirectToAction("AddProduct");
}
else
{
//when validation failed return viewmodel back to UI (View)
return View(productViewModel);
}
}

View

AddProduct.cshtml

@model ASPViewModel.ViewModels.ProductViewModel //set your ViewModel here

<div class="boxedForm">

@using (Html.BeginForm())
{
Html.EnableClientValidation(true);

<ul style="margin-top:50px;">
<li style="width: 370px">
@Html.LabelFor(m => m.ProductCategories)
@Html.DropDownListFor(m => m.SelectedValue,
new SelectList(Model.ProductCategories, "Id","CategoryName"), "-Please select a category-")
@Html.ValidationMessageFor(m => m.ProductCategory.Id)
</li>
<li style="width: 370px;margin-top:10px;">
@Html.LabelFor(m => m.ProductName)
@Html.EditorFor(m => m.ProductName)
@Html.ValidationMessageFor(m => m.ProductName)
</li>
</ul>
<div class="action">
<button class="actionButton" type="submit">
<span>Save</span>
</button>

</div>
}

</div>

Repository Methods by using the Entity


Framework
ProductRepository.cs

public class ProductRepository


{
/// <summary>
/// to get product category
/// </summary>
public ProductCategory GetProductCategory(int categoryId)
{
var productCategoryList = GetAllProductCategoriesMockDatas();

return (from p in productCategoryList


where (p.Id == categoryId)
select p).FirstOrDefault();
}

/// <summary>
/// to get all product categories
/// </summary>
/// <returns></returns>

public List<ProductCategory> GetAllProductCategories()


{
var productCategoryList = GetAllProductCategoriesMockDatas();

return (from p in productCategoryList


select p)
.OrderBy(p => p.CategoryName)
.ToList();
}

/// <summary>
/// to Get All Product Categories mock datas
/// </summary>
private List<ProductCategory> GetAllProductCategoriesMockDatas()
{
var productCategoryList = new List<ProductCategory>();

productCategoryList.Add(new ProductCategory
{
Id = 1,
CategoryName = "Foods",
});

productCategoryList.Add(new ProductCategory
{
Id = 2,
CategoryName = "Toys",

});

productCategoryList.Add(new ProductCategory
{
Id = 3,
CategoryName = "Mobile Phones",
});

return productCategoryList;
}

Вам также может понравиться