Home » Blog » .NET 6 MVC Series – Article 2

.NET 6 MVC Series – Article 2

article2

In this article, we will learn how to:

  • Pass values/data from controller to view

The data transfer from view to controller and vice versa creates a foundation for data-driven use cases.

We will learn about data transfer from view to the controller in the next article.

Send data from controller to view

  • We have already seen a way to send data from the controller to view in our first article.
  • We created a PersonViewModel, hydrated a list of models, and sent it to the view.
  • We will revisit this strategy in detail.

There are 3 ways to send data/values from the controller to view.

  • ViewData
  • ViewBag
  • Strongly typed Models

Let’s see each of these in detail.

ViewData

  • ViewData is a type of ViewDataDictionary data structure.
  • It stores key-value pairs.
  • Key can only be of type string, whereas the value can be of any type (object).
  • It’s a compilation error to denote a key other than a string.
viewdatadictionary
        public IActionResult ViewDataExample()
        {
            ViewData["Country"] = "India";
            ViewData["City"] = "New Delhi";
            ViewData["PinCode"] = 110067;

            ViewData[123] = 345;//compilation error

            return View();
        }

The above is how we assign/set a value to a ViewData.

Let’s see how can we render/get the value of a ViewData at the view level.

<h1>View Data Examples</h1>

<h3>@ViewData["Country"]</h3>
<h3>@ViewData["City"]</h3>
<h3>@ViewData["PinCode"]</h3>

We use the Razor syntax (@ symbol) along with ViewData[“Correct Key Name”]. The output is below.

viewdata-output

An incorrect key name will not result in any output, no error will be thrown.

<h3>@ViewData["PinCode123"]</h3>
Incorrect key name - there will not be any output on the view

ViewBag

  • ViewBag is a dynamic property of the abstract Controller class.
  • It also internally stores data in key-value pairs.
  • The syntax is a bit different than ViewData.
viewbag
        public IActionResult ViewBagExample()
        {
            ViewBag.FirstName = "John";
            ViewBag.LastName = "Doe";
            ViewBag.CreatedDate = DateTime.Now;
            ViewBag.IsAContractualEmployee = true;

            return View();
        }

The above is how we assign/set values to a ViewBag.

Let’s see how to render/get the values at the View level.

<h1>ViewBag Examples</h1>
@{
    var fullName = $"{@ViewBag.FirstName} {@ViewBag.LastName}";
    var employeeStatus = @ViewBag.IsAContractualEmployee == true ? "contractual employee" : "permanent employee";
}

<h3>@fullName has been an employee since @ViewBag.CreatedDate</h3>
<h4>He is a @employeeStatus</h4>

We leverage the Razor syntax and get the values from the ViewBag. The output is below.

viewbag output

A typo in the ViewBag property name does not trigger any warning, the output is just null.

Both ViewData and ViewBag are used to transfer small chunks of data, not recommended for production-level apps.

We use models to eliminate the limitations of ViewData/ViewBag, let’s learn about them.

Strongly typed Models

  • Models are nothing but classes.
  • They provide compile-time type checking.
  • Any typo or other errors are caught at compile time.
  • A view is strongly typed with the @model attribute to support the appropriate model.

Let’s create a ProductViewModel.

    public class ProductViewModel
    {
        public int Id { get; set; }
        public string ProductName { get; set; }
        public string ProductCategory { get; set; }
        public double Price { get; set; }
        public DateTime ImportedDate { get; set; }
    }

And hydrate this view model in an action method.

        public IActionResult PopulateAProduct()
        {
            var model = new ProductViewModel
            {
                Id = 100198,
                ProductName = "Intel Core i9-12900K",
                ProductCategory = "CPU",
                Price = 250000,
                ImportedDate = DateTime.Now.AddMonths(-24)
            };

            return View(model);
        }

We will create a new view (PopulateAProduct.cshtml) and will strongly bind the view with the above model, using the @model attribute.

@model ProductViewModel

<h2>Single Product Information</h2>
<section>
    <table border="1" width="500px" style="background-color:cornflowerblue;text-align:center">
        <tbody>
            <tr>
                <td>Id</td>
                <td>@Model.Id</td>
            </tr>
            <tr>
                <td>Product Name</td>
                <td>@Model.ProductName</td>
            </tr>
            <tr>
                <td>Product Category</td>
                <td>@Model.ProductCategory</td>
            </tr>
            <tr>
                <td>Product Price</td>
                <td>@Model.Price.ToString("C")</td>
            </tr>
            <tr>
                <td>Imported Date</td>
                <td>@Model.ImportedDate.ToShortDateString()</td>
            </tr>
        </tbody>
    </table>
</section>

The output is as below:

strongly-typed-view

Compile Time Type Checking in Strongly Typed Views

Let’s deliberately introduce a typo in a property name in the View and observe.

We get a direct compilation error as below.

It’s very useful to catch obvious errors beforehand.

typo
compilation error

We get IntelliSense support too while using strongly typed views.

Another example – Strongly typed view with model

Let’s see one another example of strongly typed views with a different flavor.

Let’s create a BaseProductViewModel.

    public class BaseProductViewModel
    {
        public IReadOnlyList<ProductViewModel> Products { get; set; }
        public int Count { get; set; }
        public string ConsolidatedStatus { get; set; }
    }

Let’s create a Repository class and create a method to return a static list of products.

public class Repository
    {
        public static IReadOnlyList<ProductViewModel> GetProducts()
        {
            return new List<ProductViewModel>
            {
                new ProductViewModel
                {
                    Id = 100198,
                    ProductName = "Intel Core i9-12900K",
                    ProductCategory = "CPU",
                    Price = 250000,
                    ImportedDate = DateTime.Now.AddMonths(-24)
                },
                new ProductViewModel
                {
                    Id = 110199,
                    ProductName = "AMD Ryzen 9 5950x",
                    ProductCategory = "CPU",
                    Price = 280000,
                    ImportedDate = DateTime.Now.AddMonths(-32)
                },
                new ProductViewModel
                {
                    Id = 102192,
                    ProductName = "MK LowKey70 Black RGB LED",
                    ProductCategory = "Mechanical Keyboard",
                    Price = 8000,
                    ImportedDate = DateTime.Now.AddMonths(-10)
                }
            };
        }
    }

Let’s create an action method – PopulateProducts and hydrate the BaseProductViewModel.

        public IActionResult PopulateProducts()
        {
            var products = Repository.GetProducts();
            var model = new BaseProductViewModel
            {
                Products = products,
                Count = products.Count(),
                ConsolidatedStatus = "Delivered"
            };

            return View(model);
        }

Let’s create a new view (PopulateProducts.cshtml) and consume the base model.

@model BaseProductViewModel

<h2>Products Information</h2>

<p style="background-color:greenyellow">Count of products - <strong>@Model.Count</strong></p>
<p style="background-color:red">Consolidated Status - <strong>@Model.ConsolidatedStatus</strong></p>

<h3>Delivered Product Details</h3>

<table border="1" width="100%" style="background-color:cornflowerblue;text-align:center">
    <thead>
        <tr>
            <th>Id</th>
            <th>Product Name</th>
            <th>Product Category</th>
            <th>Product Price</th>
            <th>Imported Date</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var product in Model.Products)
        {
            <tr>
                <td>@product.Id</td>
                <td>@product.ProductName</td>
                <td>@product.ProductCategory</td>
                <td>@product.Price.ToString("C")</td>
                <td>@product.ImportedDate.ToShortDateString()</td>
            </tr>
        }
    </tbody>
</table>

We have displayed all the information that the base view model provides in a meaningful way. The output is as below:

products-info

We need to be a little careful with the association of the correct class to the @model attribute, for ex: we decorated the view with BaseProductViewModel as the model, and hence we were able to consume all the information. If we would have used the ProductViewModel, we would not have been able to display the Count and ConsolidatedStatus.

Real-time MVC apps have multiple types of structures inside models, they may be nested too.

We need to stick to the basics, visualize the structure and then approach the problem.

GitHub Link

The updated code is available at – https://github.com/anurag1302/dotnet6-mvc

Conclusion

In this article, we have learned various ways to send data from a controller to a view:

  • ViewData
  • ViewBag
  • Models with strongly typed views

Stay tuned for the next article.

Tags:

2 thoughts on “.NET 6 MVC Series – Article 2”

  1. Pingback: .NET 6 MVC Series - Article 3 - TechnCode Tools

  2. Pingback: .NET 6 MVC Series - Article 4 - TechnCode Tools

Leave a Reply

Your email address will not be published. Required fields are marked *