X

How to pass multiple models to one view in Asp.net Core

Dung Do Tien May 23 2021 658
In MVC we can not pass multiple models to a single view Asp.Net Core. But in the reality, we have many case need to do this. In this article, we will discuss some ways to help resolve this problem.

1. Introduction

In this article, we will discuss how to pass many models into view in Asp.net core MVC. As you know, View in MVC only allows one model but in fact, we have many cases where we need to pass multiple models in a single view.

2. Problem need to resolve

For example, I have a page and want to display two tables of information of Books and Categories. My page require has to display as images below:

The two models with some properties:

public class BooksModel
{
    public int Id { get; set; }
    public string BookName { get; set; }
    public string Author { get; set; }
    public int CateId { get; set; }
    public DateTime PublishDate { get; set; }
}

public class CategoriesModel
{
    public int Id { get; set; }
    public string CateName { get; set; }
    public DateTime CreatedDate { get; set; }
}

And this is service help return list data of Category and Book:

public List<CategoriesModel> Categories()
{
    return new List<CategoriesModel>()
    {
        new CategoriesModel(){ Id = 1, CateName ="Programming", CreatedDate = DateTime.Now},
        new CategoriesModel(){ Id = 2, CateName ="Life", CreatedDate = DateTime.Now}
    };
}

public List<BooksModel> Books()
{
    return new List<BooksModel>() {
      new BooksModel(){ Id = 1, Author ="Mr.Jonh A", BookName="Programming in c#", CateId = 1, PublishDate = DateTime.Now },
      new BooksModel(){ Id = 2, Author ="Mr.Jonh B", BookName="Asp.net core 3 pro", CateId = 1, PublishDate = DateTime.Now },
      new BooksModel(){ Id = 3, Author ="Mr.Jonh C", BookName="Love and friend", CateId = 2, PublishDate = DateTime.Now },
      new BooksModel(){ Id = 4, Author ="Mr.Jonh D", BookName="Feeling in front wind", CateId = 2, PublishDate = DateTime.Now }
    };
}

3. How do I pass multiple models to a single view?

In fact, we have many ways to pass multiple models in a single view Asp.net MVC Core. See some common use ways below. 

3.1 Using another model 

It’s very easy to practice. I will create another model called CollectionDataModel, and it contains all the models needed to display on the view page.

public class CollectionDataModel
{
    public List<CategoriesModel> Categories { get; set; }
    public List<BooksModel> Books { get; set; }
}

And in the controller I will return as below:

public IActionResult Index()
{
    CollectionDataModel model = new CollectionDataModel();
    model.Categories = categoriesBo.Categories();
    model.Books = bookBo.Books();

    return View(model);
}

In the view code cshtml we can handle as below:

@using PassMultipleModels.Service.Models
@model CollectionDataModel

@if (Model != null)
{
    <h2>Categories</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Name</th>
                <th>Created Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var cate in Model.Categories)
            {
                <tr>
                    <td>@cate.Id</td>
                    <td>@cate.CateName</td>
                    <td>@cate.CreatedDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>


    <h2>Books</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Publish Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var book in Model.Books)
            {
                <tr>
                    <td>@book.Id</td>
                    <td>@book.BookName</td>
                    <td>@book.Author</td>
                    <td>@book.PublishDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

With this solution you can pass many models into a view very easily by adding one property to that external model, I really feel it’s so short-code. But you have to create many model files and it makes your structure code become bigger.

3.2 Using ViewBag

The ViewBag in ASP.NET MVC is used to transfer temporary data (which is not included in the model) from the controller to the view. It can't transfer data from a URL to another URL.
It's a dynamic type and skips compile-time checking. So, ViewBag property names must match in controller and view while writing it manually. ViewBag is also a good way to transfer many models in a single view in Asp.net Core. For an example: 

Code in controller

public IActionResult ViewBags()
{
    ViewBag.Categories = categoriesBo.Categories();
    ViewBag.Books = bookBo.Books();

    return View();
}

Code in view cshtml

@using PassMultipleModels.Service.Models
@{
    List<BooksModel> books = ViewBag.Books != null ? ViewBag.Books : null;
    List<CategoriesModel> categories = ViewBag.Categories != null ? ViewBag.Categories : null;
}
<h1>View bag</h1>
@if (categories != null && categories.Any())
{
    <h2>Categories</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Name</th>
                <th>Created Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var cate in categories)
            {
                <tr>
                    <td>@cate.Id</td>
                    <td>@cate.CateName</td>
                    <td>@cate.CreatedDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

@if (books != null && books.Any())
{
    <h2>Books</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Publish Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var book in books)
            {
                <tr>
                    <td>@book.Id</td>
                    <td>@book.BookName</td>
                    <td>@book.Author</td>
                    <td>@book.PublishDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

3.3 Using ViewData

ViewData also uses transform data from controller to view same with ViewBag. It is derived from the ViewDataDictionary class and is basically a Dictionary object i.e., Keys and Values where Keys are String while Values will be objects.

While retrieving the data it needs to be Type Cast to its original type as the data is stored as the object and it also requires NULL checks while retrieving. Don't forget ViewData available only for Current Requests. It will be destroyed on redirection.

ViewData is faster than ViewBag so it is also a good way to pass multiple models in a view asp.net core MVC. Let see an example:

Code in controller

public IActionResult ViewDatas()
{
    ViewData["Categories"] = categoriesBo.Categories();
    ViewData["Books"] = bookBo.Books();

    return View();
}

Code in view cshtml

@using PassMultipleModels.Service.Models
@{
    List<BooksModel> books = ViewData["Books"] != null ? (List<BooksModel>)ViewData["Books"] : null;
    List<CategoriesModel> categories = ViewData["Categories"] != null ? (List<CategoriesModel>)ViewData["Categories"] : null;
}

<h1>View data</h1>

@if (categories != null && categories.Any())
{
    <h2>Categories</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Name</th>
                <th>Created Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var cate in categories)
            {
                <tr>
                    <td>@cate.Id</td>
                    <td>@cate.CateName</td>
                    <td>@cate.CreatedDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

@if (books != null && books.Any())
{
    <h2>Books</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Publish Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var book in books)
            {
                <tr>
                    <td>@book.Id</td>
                    <td>@book.BookName</td>
                    <td>@book.Author</td>
                    <td>@book.PublishDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

I really don't usually use ViewData as the ViewBag, because I need to cast models before using it. But I think it is a good way to do it.

3.4 Using Tuple

A Tuple object is an immutable, fixed-size and ordered sequence object. It is a data structure that has a specific number and sequence of elements. The .NET framework supports tuples upto seven elements.

Using this tuple object we can pass multiple models from the controller to the view.

Code in controller

public IActionResult Tuples()
{
    var categories = categoriesBo.Categories();
    var books = bookBo.Books();

    var tupleModel = new Tuple<List<CategoriesModel>, List<BooksModel>>(categories, books);
    return View(tupleModel);
}

Code in view cshtml

@using PassMultipleModels.Service.Models
@model Tuple<List<CategoriesModel>, List <BooksModel>>

<h1>Tuple</h1>

@if (Model != null)
{
    <h2>Categories</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Name</th>
                <th>Created Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var cate in Model.Item1)
            {
                <tr>
                    <td>@cate.Id</td>
                    <td>@cate.CateName</td>
                    <td>@cate.CreatedDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>


    <h2>Books</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Publish Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var book in Model.Item2)
            {
                <tr>
                    <td>@book.Id</td>
                    <td>@book.BookName</td>
                    <td>@book.Author</td>
                    <td>@book.PublishDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

3.5 Using Dynamic Model

ExpandoObject (the System.Dynamic namespace) is a class that was added to the .Net Framework 4.0 that allows us to dynamically add and remove properties onto an object at runtime. Using this ExpandoObject, we can create a new object and can add our list of categories and books into it as a property. We can pass this dynamically created object to the view and render list of the categories and books.

With a dynamic model, you can transfer many models to a view very easily.

Code in controller

public IActionResult DynamicModel()
{
    dynamic resultModel = new ExpandoObject();

    resultModel.Categories = categoriesBo.Categories();
    resultModel.Books = bookBo.Books();

    return View(resultModel);
}

Code in view cshtml

@using PassMultipleModels.Service.Models

<h1>Dynamic model</h1>

@if (Model != null)
{
    <h2>Categories</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Name</th>
                <th>Created Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (CategoriesModel cate in Model.Categories)
            {
                <tr>
                    <td>@cate.Id</td>
                    <td>@cate.CateName</td>
                    <td>@cate.CreatedDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>


    <h2>Books</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 70px;">Id</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Publish Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (BooksModel book in Model.Books)
            {
                <tr>
                    <td>@book.Id</td>
                    <td>@book.BookName</td>
                    <td>@book.Author</td>
                    <td>@book.PublishDate.ToString("MMM dd yyyy")</td>
                </tr>
            }
        </tbody>
    </table>
}

4. Conclusion

We still have some other ways to pass multiple models to a view in Asp.net Core MVC but I think some ways above are the best way to do it.
If you need source code to refer you can download it from Github here.
Happy code!!!