locked
MVC Question RRS feed

  • Question

  • User-195907812 posted

    I'm starting to learn MVC (from webforms).

    To make things clearer in my own mind, for now I just concentrating on the model/view/controller flow - I'm not adding any data (such as entity framework etc) just to keep things simple while I experiment.

    Am I correct in thinking that the model is only for page structure? For example, I'd have the following model:

    public class MyFirstPageModel
    {
         public string H1Heading { get; set; }
         public string HeaderImagePath { get; set; }
         public string HeaderImageDescription { get; set; }
    }

    Then in the view I'd have:

    @model MyFirstPageModel

    <h1>@Model.H1Heading</h1>
    <img src="@Model.HeaderImagePath" alt="@Model.HeaderImageDescription" />

    So, if I wanted to populate this view's "data", I do this in the controller:

    public ActionResult ExampleView()
    {
         Models.MyFirstPageModel tmpModel01 = new Models.MyFirstPageModel {
              H1Heading = "My Page Title",
              HeaderImagePath = "/myurl/",
              HeaderImageDescription = "my desc"
         };

         return View(tmpModel01);
    }

    My question is (assuming the above is correct), if (as an example) I did now want to add data from my MySQL database, the correct place to do this would be inside of the controller, something like...

    public ActionResult ExampleView()
    {
         string sql = "SELECT * FROM mytable WHERE page='test' LIMIT 1";
         DataSet dsData = DAL.getData(sql);

         Models.MyFirstPageModel tmpModel01 = new Models.MyFirstPageModel {
              H1Heading = dsData["title"].ToString(),
              HeaderImagePath = "/myurl/",
              HeaderImageDescription = "my desc"
         };

         return View(tmpModel01);
    }

    Many thanks

    Monday, December 10, 2018 2:57 PM

All replies

  • User-474980206 posted

    You are correct. The model is the object data passed to the view. The view should use this data to render the html. You can also pass data via the ViewBag collection, by a typed model is a better choice.

    There is also a post back model used with a form post to send data from the view to the controller. This post back model will only be the form field values. It’s common to use the same model type for post back but not required. The model binding of the post
    back is based on the name of the form fields.
    Monday, December 10, 2018 3:13 PM
  • User475983607 posted

    RageRiot

    Am I correct in thinking that the model is only for page structure?

    That's correct.  The "Model" in MVC is dynamic data.  The View knows how to render the data sorta' like a Model template. 

    RageRiot

    So, if I wanted to populate this view's "data", I do this in the controller:

    Correct, the Controller is responsible for populating the Model.

    RageRiot

    My question is (assuming the above is correct), if (as an example) I did now want to add data from my MySQL database, the correct place to do this would be inside of the controller, something like...

    This is the part that confuses developers.  The Model in MVC is not the same as the Model used to fetch or update data in the DB.  Commonly, there is a layer between the MVC app and data layer that that converts the DB model to an MVC model.  This can also be done in the controller but usually there's a layer that handles this.

    You'll find as you get more experience that the DB interface requires slightly different data than the UI.  A good example is a dropdown or a page that displays data coming from many tables like a dashboard.

    Monday, December 10, 2018 3:17 PM
  • User-821857111 posted

    Just to clarify about Models in ASP.NET MVC, the model passed to the view is usually also referred to as the ViewModel. Your MyFirstPageModel is an example of a ViewModel. It only contains properties for the view. There is another model - the domain model. This usually consists of classes that represent the entities in your application, and they usually map to database tables. You are using ADO.NET DataTables and DataSets in your example code, so this layer won't exist in your case.

    Monday, December 10, 2018 4:11 PM
  • User1120430333 posted

    I always look at it this way when it comes to ASP.NET MVC.

    https://en.wikipedia.org/wiki/Separation_of_concerns

    https://www.c-sharpcorner.com/UploadFile/56fb14/understanding-separation-of-concern-and-Asp-Net-mvc/

    https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/overview/understanding-models-views-and-controllers-cs

    <copied>

    An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic. For example, if you are using the Microsoft Entity Framework to access your database, then you would create your Entity Framework classes (your .edmx file) in the Models folder.

    A view should contain only logic related to generating the user interface. A controller should only contain the bare minimum of logic required to return the right view or redirect the user to another action (flow control). Everything else should be contained in the model.

    In general, you should strive for fat models and skinny controllers. Your controller methods should contain only a few lines of code. If a controller action gets too fat, then you should consider moving the logic out to a new class in the Models folder.

    <end>

    https://dzone.com/articles/reasons-move-datatables

    http://lauteikkehn.blogspot.com/2012/03/datatable-vs-list.html

    https://www.codingblocks.net/programming/boxing-and-unboxing-7-deadly-sins/

     

    Monday, December 10, 2018 8:25 PM
  • User1520731567 posted

    Hi RageRiot,

    Yes,you are right.

    You could use EF or ADO.NET to get data from database and populate model with these data,pass model between controller and view.

    If you would like to pass multi-line data,you need to use a strong type,like:

    @model <namesapce>.<filepath>.List<MyFirstPageModel>

    And in controller, you should also populate List<MyFirstPageModel> to make Controller and View correspond.

    You can make a simple example yourself,and add some breakpoints at your backend,and open F12 developer tool to check your front end.

    Best Regards.

    Yuki Tao

    Tuesday, December 11, 2018 9:09 AM
  • User-195907812 posted

    Thank you all for your input, I'm glad I'm heading in the right direction.

    I will of course add data at a later point, but for now I'm just trying to convert years of webforms knowledge to MVC thinking and I'm finding it quite a challenge.

    Any thoughts on the 'skinny controller/fat models' comment from DA924?

    Tuesday, December 11, 2018 9:16 AM
  • User-821857111 posted

    Any thoughts on the 'skinny controller/fat models' comment from DA924?

    The idea behind that epithet is to keep the code in your controller classes to a minimum. 

    Typically, Web Forms apps are characterised by a lot of processing code in the Code Behind file. When people first move across to MVC, they are tempted to replicate that approach in a controller action method. Ideally, you should start to separate data access, business logic etc out into different layers, and then the controller should only ever call methods in those layers rather than create database connections etc.

    I wrote an article on this kind of thing some time ago that shows how to use ADO.NET in an MVC application: https://www.mikesdotnetting.com/article/132/asp-net-mvc-is-not-all-about-linq-to-sql. Note - it's so old that it uses the Web Forms view engine, which has been replaced by Razor now.

    Tuesday, December 11, 2018 10:48 AM
  • User1120430333 posted

    I'll give you an example.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using Microsoft.AspNetCore.Mvc.Rendering;
    
    namespace ProgMgmntCore2UserIdentity.Models
    {
        public class TaskViewModels
        {
            public class TaskCreate
            {
                public int TaskId { get; set; }
    
                [Required(ErrorMessage = "Task Name is required")]
                [StringLength(50)]
                public string TaskName { get; set; }
                [Required(ErrorMessage = "Note is required")]
                [StringLength(2000)]
                public string Note { get; set; }
    
                [Required(ErrorMessage = "Start Date is required")]
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? StartDate { get; set; }
                [Required(ErrorMessage = "Resource is required")]
                public string ResourceId { get; set; }
                public int ProjectId { get; set; }
                [Required(ErrorMessage = "Duration is required")]
                public string TaskDuration { get; set; }
                [Required(ErrorMessage = "Status is required")]
                public string Status { get; set; }
    
                public List<SelectListItem> Statuses { get; set; }
                public List<SelectListItem> Durations { get; set; }
                public List<SelectListItem> Resources { get; set; }
            }
    
            public class TaskEdit
            {
                public int TaskId { get; set; }
    
                [Required(ErrorMessage = "Task Name is required")]
                [StringLength(50)]
                public string TaskName { get; set; }
                [Required(ErrorMessage = "Note is required")]
                [StringLength(2000)]
                public string Note { get; set; }
    
                [Required(ErrorMessage = "Start Date is required")]
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? StartDate { get; set; }
               
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? EndDate { get; set; }
    
                public string ResourceId { get; set; }
                public int ProjectId { get; set; }
                public string TaskDuration { get; set; }
                public string TaskSpent { get; set; }
                public string Status { get; set; }
    
                public List<SelectListItem> Statuses { get; set; }
                public List<SelectListItem> Durations { get; set; }
                public List<SelectListItem> Resources { get; set; }
                public List<SelectListItem> Spents { get; set; }
            }
            public List<TaskEdit> Tasks { get; set; }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Entities;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.Extensions.Caching.Memory;
    using ProgMgmntCore2UserIdentity.WebApi;
    
    namespace ProgMgmntCore2UserIdentity.Models
    {
        public class TaskModel :ITaskModel
        {
            private readonly IMemoryCache _memoryCache;
            private readonly IWebApi _webApi;
    
            public TaskModel(IWebApi webApi, IMemoryCache memoryCache)
            {
                _webApi =  webApi;
                _memoryCache = memoryCache;
            }
    
            public TaskViewModels GetTasksByProjectId(int id)
            {
                var vm = new TaskViewModels { Tasks = new List<TaskViewModels.TaskEdit>() };
    
                var dtos = _webApi.GetTasksByProjIdApi(id).ToList();
    
                vm.Tasks.AddRange(dtos.Select(dto => new TaskViewModels.TaskEdit
                {
                    TaskId = dto.TaskId,
                    TaskName = dto.TaskName,
                    Note = dto.Note,
                    StartDate = dto.StartDate,
                    EndDate = dto.EndDate,
                    ResourceId = dto.ResourceId,
                    ProjectId = dto.ProjectId,
                    TaskDuration = dto.TaskDuration,
                    Status = dto.Status
                }).ToList());
    
                return vm;
            }
    
            public TaskViewModels.TaskCreate Create()
            {
                var task = new TaskViewModels.TaskCreate();
                
                return (TaskViewModels.TaskCreate)PopulateDropDownLists(task, "Create");
            }
    
            public void Create(TaskViewModels.TaskCreate task, int id)
            {
                var dto = new DtoTask
                {
                    TaskId = task.TaskId,
                    TaskName = task.TaskName,
                    Note = task.Note,
                    StartDate = (DateTime) task.StartDate,
                    ResourceId = task.ResourceId,
                    ProjectId = id,
                    TaskDuration = task.TaskDuration,
                    TaskSpent = "",
                    Status = task.Status
                };
    
                _webApi.CreateTaskApi(dto);
            }
    
            public TaskViewModels.TaskEdit Edit(int id)
            {
                var dto = _webApi.GetTaskByIdApi(id);
    
                var task = new TaskViewModels.TaskEdit()
                {
                    TaskId = dto.TaskId,
                    TaskName = dto.TaskName,
                    Note = dto.Note,
                    StartDate = dto.StartDate,
                    EndDate = dto.EndDate,
                    ResourceId = dto.ResourceId,
                    ProjectId = dto.ProjectId,
                    TaskDuration = dto.TaskDuration,
                    TaskSpent = dto.TaskSpent,
                    Status = dto.Status
                };
    
                return (TaskViewModels.TaskEdit)PopulateDropDownLists(task, "Edit");
            }
    
            public void Edit(TaskViewModels.TaskEdit task)
            {
                if (task.StartDate != null)
                {
                    var dto = new DtoTask
                    {
                        TaskId = task.TaskId,
                        TaskName = task.TaskName,
                        Note = task.Note,
                        StartDate = (DateTime) task.StartDate,
                        EndDate = task.EndDate,
                        ResourceId = task.ResourceId,
                        ProjectId = task.ProjectId,
                        TaskDuration = task.TaskDuration,
                        TaskSpent = task.TaskSpent,
                        Status = task.Status
                    };
    
                    _webApi.UpdateTaskApi(dto);
                }
            }
    
            public void Delete(int id)
            {
                _webApi.DeleteTaskApi(new DtoId{Id = id});
            }
    
            public Object PopulateDropDownLists(object task, string type)
            {
                TaskViewModels.TaskEdit taskedit;
                TaskViewModels.TaskCreate taskcreate;
                Object obj;
    
                bool isExist = _memoryCache.TryGetValue("DtoCache", out DtoCache dtocache);
    
                if (!isExist)
                {
                    dtocache = _webApi.GetCacheApi();
    
                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                        .SetSlidingExpiration(TimeSpan.FromSeconds(30));
    
                    _memoryCache.Set("DtoCache", dtocache, cacheEntryOptions);
                }
    
                if (type == "Create")
                {
                    taskcreate = (TaskViewModels.TaskCreate) task;
    
                    taskcreate.Statuses = new List<SelectListItem>();
    
                    foreach (var st in dtocache.Statuses)
                    {
                        var sli = new SelectListItem { Value = st.Value, Text = st.Text };
                        taskcreate.Statuses.Add(sli);
                    }
    
                    var selectedtask = (from a in taskcreate.Statuses.Where(a => a.Value == taskcreate.Status) select a)
                        .SingleOrDefault();
    
                    if (selectedtask != null)
                        selectedtask.Selected = true;
    
                    taskcreate.Durations = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskcreate.Durations.Add(sli);
                    }
    
                    if (taskcreate.TaskDuration != null)
                    {
                        var selectedduration = (from a in taskcreate.Durations.Where(a => a.Value == taskcreate.TaskDuration) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskcreate.Resources = new List<SelectListItem>();
    
                    foreach (var rs in dtocache.Resources)
                    {
                        var sli = new SelectListItem { Value = rs.Value, Text = rs.Text };
                        taskcreate.Resources.Add(sli);
                    }
    
                    if (taskcreate.ResourceId != null)
                    {
                        var selectedresource = (from a in taskcreate.Resources.Where(a => a.Value == taskcreate.ResourceId) select a)
                            .SingleOrDefault();
    
                        if (selectedresource != null)
                            selectedresource.Selected = true;
                    }
    
                    obj = taskcreate;
                }
                else
                {
                    taskedit = (TaskViewModels.TaskEdit) task;
    
                    taskedit.Statuses = new List<SelectListItem>();
    
                    foreach (var st in dtocache.Statuses)
                    {
                        var sli = new SelectListItem { Value = st.Value, Text = st.Text };
                        taskedit.Statuses.Add(sli);
                    }
                  
                    var selectedtask = (from a in taskedit.Statuses.Where(a => a.Value == taskedit.Status) select a)
                        .SingleOrDefault();
    
                    if (selectedtask != null)
                        selectedtask.Selected = true;
    
                    taskedit.Durations = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskedit.Durations.Add(sli);
                    }
                    
                    if (taskedit.TaskDuration != null)
                    {
                        var selectedduration = (from a in taskedit.Durations.Where(a => a.Value == taskedit.TaskDuration) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskedit.Spents = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskedit.Spents.Add(sli);
                    }
    
                    if (taskedit.TaskSpent != null)
                    {
                        var selectedduration = (from a in taskedit.Spents.Where(a => a.Value == taskedit.TaskSpent) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskedit.Resources = new List<SelectListItem>();
    
                    foreach (var rs in dtocache.Resources)
                    {
                        var sli = new SelectListItem { Value = rs.Value, Text = rs.Text };
                        taskedit.Resources.Add(sli);
                    }
    
                    if (taskedit.ResourceId != null)
                    {
                        var selectedresource = (from a in taskedit.Resources.Where(a => a.Value == taskedit.ResourceId) select a)
                            .SingleOrDefault();
    
                        if (selectedresource != null)
                            selectedresource.Selected = true;
                    }
    
                    obj = taskedit;
                }
        
                return obj;
            }
        }
    }
    using System;
    using System.Linq;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using ProgMgmntCore2UserIdentity.Models;
    
    namespace ProgMgmntCore2UserIdentity.Controllers
    {
        public class TaskController : Controller
        {
            private readonly ITaskModel _taskModel;
            private readonly IProjectModel _projectModel;
            private readonly IModelHelper _modelHelper;
    
            public TaskController(ITaskModel taskModel, IProjectModel projectModel, IModelHelper modelHelper)
            {
                _taskModel = taskModel;
                _projectModel = projectModel;
                _modelHelper = modelHelper;
            }
    
            // GET: Task
            [Authorize]
            public ActionResult Index(int id)
            {
                var project = _projectModel.GetProjectById(id);
    
                TempData["ProjectId"] = id;
                TempData["Title"] = project.ProjectName;
    
                return View(_taskModel.GetTasksByProjectId(id));
            }
    
            [Authorize]
            public ActionResult Details(int id = 0)
            {
                return id == 0 ? null : View(_taskModel.Edit(id));
            }
    
            [Authorize]
            public ActionResult Create()
            {
                return View(_taskModel.Create());
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Create(TaskViewModels.TaskCreate task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                ValidateddlStatuses();
                ValidateddlDurations();
                ValidateddlResources();
               
                task.Status = (Request.Form["ddlStatusTypes"]);
                task.TaskDuration = (Request.Form["ddlDurations"]);
                task.ResourceId = (Request.Form["ddlResources"]);
    
                if (!ModelState.IsValid) return View((TaskViewModels.TaskCreate)_taskModel.PopulateDropDownLists(task, "Create"));
    
                _taskModel.Create(task, (int)TempData["ProjectId"]);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"] });
            }
    
            [Authorize]
            public ActionResult Edit(int id = 0)
            {
                return id == 0 ? null : View(_taskModel.Edit(id));
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Edit(TaskViewModels.TaskEdit task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                if (ModelState.IsValid && _modelHelper.IsEndDateLessThanStartDate(task, "Task"))
                {
                    ModelState.AddModelError(String.Empty, "End Date cannot be less than Start Date.");
                }
    
                if (!ModelState.IsValid) return View( (TaskViewModels.TaskEdit)_taskModel.PopulateDropDownLists(task, "Edit"));
    
                var thetask = new TaskViewModels.TaskEdit();
    
                thetask = task;
    
                thetask.ProjectId = (int) TempData["ProjectId"];
    
                _taskModel.Edit(thetask);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
            }
    
            public ActionResult Delete(int id = 0)
            {
                if (id > 0) _taskModel.Delete(id);
    
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"] });
            }
    
            public ActionResult Cancel()
            {
                var id = TempData["ProjectId"];
    
                return RedirectToAction("Index", "Home");
            }
    
            public ActionResult UploadFile(int id)
            {
                return RedirectToAction("Index", "Upload", new { id = id, type = "TM" });
            }
    
            private void ValidateddlStatuses()
            {
                if (Request.Form["ddlStatusTypes"] == string.Empty)
                   return;
    
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "Status") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
    
            private void ValidateddlDurations()
            {
                if (Request.Form["ddlDurations"] == string.Empty)
                   return;
    
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "TaskDuration") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
    
            private void ValidateddlResources()
            {
                if (Request.Form["ddlResources"] == string.Empty)
                   return;
                
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "ResourceId") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
        }
    }

    Tuesday, December 11, 2018 10:55 AM
  • User-195907812 posted

    Thank you DA924, great example.

    Thanks everyone.

    Tuesday, December 11, 2018 2:42 PM