Friday, July 4, 2014

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
  • For 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 :


ViewModel

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 :

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)

ViewModels that resides in the root of the project

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 youll 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 ( How to Use ValueInjecter ? )


Lets Try with Simple Example

        - C#,MVC 3 and Visual Studio 2010 has been used.
        - Please follow an Inline Comments on Code. 
        - Simple application with product category drop down list, product name text box
          and Save button   

Our Domain Models Look Like Below

 public class Product
    {
        public Product() { Id = Guid.NewGuid(); Created = DateTime.Now; }
        public Guid Id { get; set; }
        public string ProductName { getset; }
        
        public virtual ProductCategory ProductCategory { getset; }
    }

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

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


Our ViewModel Looks Like Below

 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; }
    }

Our Controller Action Methods Look Like Below

        [HttpGet]
        public ActionResult AddProduct() //generate view with categories for enter product data
        {
            //for get product categories from database
            var prodcutCategories = Repository.GetAllProductCategories();

            //for initialize viewmodel
            var productViewModel = new ProductViewModel();
            
            //assign values for viewmodel
            productViewModel.ProductCategories = prodcutCategories;

            //send viewmodel into UI (View)
            return View("AddProduct", productViewModel);
        }

        [HttpPost]
        public ActionResult AddProduct(ProductViewModel productViewModel) //save entered data
        {
            //get product category for selected drop down list value
            var prodcutCategory = Repository.GetProductCategory(productViewModel.SelectedValue);
            
            //for get all product categories
       var prodcutCategories = Repository.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
            {
                //save recived data into database
                Repository.AddProduct(productObj);
                return RedirectToAction("AddProduct");
            }
            else
            {
                //when validation failed return viewmodel back to UI (View) 
                return View(productViewModel);
            }
        }

Our View Looks Like Below

Our View


AddProduct.cshtml

@model YourProject.ViewModels.ProductViewModel        //set your viewmodel here

 <div class="boxedForm">
  
@using (Html.BeginAbsoluteRouteForm("add", new { action = "AddProduct"},FormMethod.Post }))
         {
             <ul>
                     <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">
                  @Html.CompleteEditorFor(m => m.ProductName, labelOverride: "Product Name")
                  @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

--- GetProductCategory() ---

//for get product category
public ProductCategory GetProductCategory(int categoryId)
{
return (from productCategory in Catalog.ProductCategories
where (productCategory.Id == categoryId)
select productCategory).FirstOrDefault();
}

---GetAllProductCategories()---

//for get all product categories
public List<ProductCategory> GetAllProductCategories()
{
return (from productCategory in Catalog.ProductCategories
select productCategory)
.OrderBy(p => p.CategoryName)
.ToList();
}


Conclusion
  • ViewModels help you organize and manage data in MVC applications when you need to work with more complex data than the other objects allow.
  • Using ViewModels gives you the flexibility to use data as you see fit.
  • ViewModels are generally a more flexible way to access multiple data sources than domain models.
  • Even for simple scenarios always try to use ViewModel approach to maintain consistency in Your coding practices.

I hope this helps to You.Comments and feedback greatly appreciated.

Happy Coding.

Related Article

How to Use ValueInjecter with Asp.net MVC ViewModel ?

Related Posts by Categories

0 comments: