Monday, October 29, 2012

How to Use ViewModel with ASP.NET MVC

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


How to Use ViewModel with ASP.NET MVC


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

How to Use ViewModel with ASP.NET MVC


Where Should We Create ViewModel (physically)

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

How to Use ViewModel with ASP.NET MVC

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


How to Use ViewModel with ASP.NET MVC


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.SelectedValue);

        //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

How to Use ViewModel with ASP.NET MVC



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


    }


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.

If you feel it was a good article,Give me a +1.Thank You.