Building a Hotel Management System With ASP.NET Core(#4) - Building the Room Controller Logic

in #utopian-io3 years ago (edited)

Repository

https://github.com/dotnet/core

What Will I Learn?

  • You will learn how to create the application logic for the Room Controller of our Hotel System.
  • You will learn about ViewBags/ViewData and how they are used to pass data between Controller and View.
  • You will learn about ViewModels, why they are needed in building ASP.NET MVC applications and you will implement one.
  • You will learn how to make use of a SelectList in ASP.NET MVC.
  • You will learn to perform controller CRUD operations in ASP.NET Core.

Requirements

  • Basic knowledge of C# programming language
  • Visual Studio 2017/ VS code/ Any suitable code editor
  • Previous Tutorial

Difficulty

  • Intermediate/Advanced

Tutorial Contents

img8.PNG

In the previous tutorial, we began writing our application logic beginning with the RoomType which represents the room categories by which the rooms in our hotel are classified. In this tutorial, we will write the application logic for our room controller. Recall that these controllers are projections of our defined model entities.
Please read the previous tutorial post as we will be building on some concepts mentioned therein.

Creating Our Controller

Right Click on your Controller folder and Add a new controller with the following selected
Screenshot (374).png

Capture.PNG

Select the model class as Room and the Data class as ApplicationDbContext

Capture.PNG

(1) Index Action

The Index action of our room controller takes a different fashion from what we saw in the previous controller. In the RoomType index view, we simply returned a list of the room types. However, in the Room Index view, our intention is to return a list of all the rooms present in our hotel as well as a list of all the room types so we can sort our rooms based on the room type category they belong to.

Passing Two Models to a View

To achieve the task stated above, we would need to supply to our view two distinct models. There are two ways we can achieve this in ASP.NET MVC and I will show both of them.

  • Using ViewBags/ViewData

All along we have been passing data from the controller to the view using strongly typed models. ViewBags in MVC are a way to loosely pass data from the controller to the view. We can embed anything in a viewbag and pass it to the view, from the controller. In the case of our system, we would need to define the context class so we can access the RoomTypes DbSet.

Edit your controller as follows:

  • Add an ApplicationDbContext property
 private readonly ApplicationDbContext _context;
  • Make this property a parameter in your controller constructor
   public RoomsController(IGenericHotelService<Room> hotelService, ApplicationDbContext context)
        {
            _hotelService = hotelService;
            _context = context;
        }

Now in your index Action,

public async Task<IActionResult> Index()
        {
            ViewBag.RoomTypes = _context.RoomTypes.ToList();
            return View(await _hotelService.GetAllItemsAsync());
        }

Note that it is also possible to replace ViewBag.RoomTypes with ViewData["RoomTypes"]. They are just two different ways of writing the same thing

To view the result of what we just performed, run the solution in debug mode by pressing F5. Set a breakpoint at the line of the ViewBag and then navigate to /rooms/index

We notice that, hovering over the ViewBag variable or observing the Auto monitor, ViewBag.RoomTypes contains three items in it, all of type RoomType
Screenshot (379).png

You can enable the auto window by clicking on Debug --> Windows --> Auto

  • Now, in our view, all we need to access this list is to simply call @ViewBag.RoomTypes. We can iterate through the list by
@foreach(var roomType in ViewBag.RoomTypes as IEnumerable<RoomType>)
      {

        //Perform required action

       }

It is obvious that this is a very easy and convenient method of passing data to and from the controller. However, this method has its many disadvantages. First off, it defeats the purpose for which we isolated the database fetching logic to a separate layer. As can be seen from the controller constructor, we now pass in the application context which the controller was never supposed to interact with directly. Also, we loose intelliscence when working with ViewBag content. We might then run into error scenarios where we can't pin point where exactly the error is coming from.

For these reasons, this method is not convenient to fetch the RoomType DbSet.

Using ViewModels

In this method, we create a ViewModel which contains two properties corresponding to a list of each of these models (Room and RoomType). A ViewModel is a model made specifically for use in a particular view.

  • Create a New Folder -- ViewModels
  • Create a new Class -- RoomsAdminIndexViewModel.cs and put in the following code
public class RoomsAdminIndexViewModel
    {
        public List<Room> Rooms { get; set; }
        public List<RoomType> RoomTypes { get; set; }
    }

Because what we want to achieve is beyond the scope of our GetAllItemsAsync() method in the IGenericHotelService implementation which returns only a list of a particular model, we would need to define a new method to handle this special FETCH request to return two DbSets from the database. We do this as follows:

  • Add a New Method to IGenericHotelService

RoomsAdminIndexViewModel GetAllRoomsAndRoomTypes();
  • Implement this New Method in GenericHotelService

  public RoomsAdminIndexViewModel GetAllRoomsAndRoomTypes()
        {

            var rooms = _context.Rooms.ToList();
            var roomtypes = _context.RoomTypes.ToList();

            var RoomsAdminIndeViewModel = new RoomsAdminIndexViewModel
            {
                Rooms = rooms,
                RoomTypes = roomtypes
            };
            return RoomsAdminIndeViewModel;
        }
  • Controller Method

Having done all these, in our controller method, we make a call to the service method.

  public  IActionResult Index()
        {
            return View(_hotelService.GetAllRoomsAndRoomTypes());            
        }
  • When we debug now, with our breakpoint set, we find out that our model contains the required DbSets.
  • Updating Our View

Having altered the returning model, we have to update our view to reflect the changes.

First we change our model definition for the view

@model TheHotelApp.ViewModels.RoomsAdminIndexViewModel

Our table is updated as follows:

<table class="table">
    <thead>
        <tr>
            <th>Number</th>
            <th>Price</th>
            <th>Availability</th>
            <th>Description</th>
            <th>Maximum Guests</th>
            <th>Room Category</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Rooms)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Number)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Available)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Description)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.MaximumGuests)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.RoomType.Name)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>
  • The most significant change from what was present previously is the array that we are now looping against
    @foreach (var item in Model.Rooms)

To illustrate that RoomTypes DbSet is present, let us return a list of all the RoomType names:

@foreach (var item in Model.RoomTypes)
{
    <div>
        <p>@item.Name</p>
    </div>
}

When we run our project and navigate to localhost:port/rooms we get the anticipated result.

(2) Create Action

The Create action, just like the index action has a slight variation from the implementation in the RoomType controller.
Recall that the Room Entity has a one-one relationship with the RoomType. We need to provide a way for the user to select a RoomType entity that corresponds to this property. To achieve that, we need to provide the user with a list of all the RoomTypes and have him/her select one.

Just like the former, this result cannot be achieved with our standard defined Service Methods. Since we specified TEntity as Room, our GetAllItemsAsync() can only fetch Room Entities and not RoomType Entities. In order to fetch RoomType entities, we need to create a new dedicated method for this.

Just like we did in the above,

  • Add a New Method to IGenericHotelService

Task<IEnumerable<RoomType>> GetAllRoomTypesAsync(); 
  • Implement this New Method in GenericHotelService

public async Task<IEnumerable<RoomType>> GetAllRoomTypesAsync()
        {
            return await _context.RoomTypes.ToArrayAsync();
        }
  • Create Action Method Implementation

Having done all these, in our controller method, we make a call to the service method and pass it into a SelectList, which is sent down to the view via a ViewData.

  public IActionResult Create()
        {
            var RoomTypes = _hotelService.GetAllRoomTypesAsync().Result;
            ViewData["RoomTypeID"] = new SelectList(RoomTypes, "ID", "Name");
            return View();
        }

(3) Edit Action

  • The Edit action just like the Create Action implements the GetAllRoomTypesAsync() service method to supply a ViewBag containing the List of RoomTypes to the view.
  • The POST implementation remains the same as that in the previous tutorial.

(4) Details Action

  • We implement the details action just like we did in the previous tutorial on RoomType Controller. However, in subsequent tutorial, we are going to make changes to this method so we can display the list of foreign properties such as features and images associated with a particular room.

(5) Delete Action

  • The Delete action again is similar to that done in the previous tutorial.

To prevent repetition and also to avoid making this post too cumbersome that you miss the main points, I would not write out the code for those actions. If you have trouble implementing any of them, do well to check the github repo for this tutorial to put yourself right back on track
https://github.com/Johnesan/TheHotelApplication/blob/master/TheHotelApp/Controllers/RoomsController.cs

Curriculum

Proof of Work Done

Github Repo for the tutorial solution:
https://github.com/Johnesan/TheHotelApplication

Sort:  

Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend few advices for your upcoming contributions:

  • There are parts of the code that have little explanation, try to explain as much as possible.
  • In the print screens try to be more professional. Place a red box in the area where you want to indicate.

Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian rules and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post,Click here


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Hey @johnesan
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!