Alec Menconi
Alec Menconi

Reputation: 745

MVC 4 Strongly Typed Views Not Passing Model For Controller

Not long ago I built a maintenance request feature in my current MVC 4 project. It worked out perfectly so when I needed another similar tooling request feature added I followed the same composition just with slightly modified tables and model properties etc. Now for some reason within the tooling request controller none of the model binding seems to work (each httppost method receives a null value for the model being passed).

For the feature I've created 3 models. Essentially one holds a list of all the requests, one is the model for each request itself with a list of notes associated with it and then the third is the note model.

Models:

    namespace TeamPortal.Models
{
    public class ToolingRequestModels
    {
        public List<ToolingRequestModel> ToolingRequests { get; set; }
        public SelectList Sources { get; set; }
        public SelectList Assignees { get; set; }
        public SelectList DefectTypes { get; set; }

        public ToolingRequestModels()
        {
            TeamPortalService TPsvc = new TeamPortalService();

            Sources = new SelectList(TPsvc.GetToolingRequestSources(), "Text", "Value");
            Assignees = new SelectList(TPsvc.GetToolingRequestAssignees(), "Text", "Value");
            DefectTypes = new SelectList(TPsvc.GetToolingRequestDefectTypes(), "Text", "Value");
        }

        public ToolingRequestModels(string ListType)
        {
            ToolingRequestModels baseModel = new ToolingRequestModels();

            TeamPortalService TPsvc = new TeamPortalService();

            List<int> RequestIDList = TPsvc.GetToolingRequests(ListType);

            List<ToolingRequestModel> tempToolingRequests = new List<ToolingRequestModel>();
            foreach (int requestID in RequestIDList)
            {
                ToolingRequestModel tempToolingRequestModel = new ToolingRequestModel(requestID, baseModel.Sources, baseModel.Assignees, baseModel.DefectTypes);
                tempToolingRequests.Add(tempToolingRequestModel);
            }
            ToolingRequests = tempToolingRequests;
        }
    }

    public class ToolingRequestModel
    {
        public int RequestID { get; set; }
        public string Requester { get; set; }
        public DateTime RequestDate { get; set; }
        public string Summary { get; set; }

        public SelectList Sources { get; set; }
        public string Source { get; set; }

        public string Description { get; set; }

        public SelectList Assignees { get; set; }
        public string AssignedTo { get; set; }

        public SelectList DefectTypes { get; set; }
        public string DefectType { get; set; }

        public string Status { get; set; }
        public string Model { get; set; }
        public DateTime? CompletionDate { get; set; }
        public bool CloseRequest { get; set; }

        public string AddNote { get; set; }
        public List<ToolingNoteModel> Notes { get; set; }

        public ToolingRequestModel()
        {
            TeamPortalService TPsvc = new TeamPortalService();

            Sources = new SelectList(TPsvc.GetToolingRequestSources(), "Text", "Value");
            Assignees = new SelectList(TPsvc.GetToolingRequestAssignees(), "Text", "Value");
            DefectTypes = new SelectList(TPsvc.GetToolingRequestDefectTypes(), "Text", "Value");
        }

        public ToolingRequestModel(int requestID, SelectList slSources, SelectList slAssignees, SelectList slDefectTypes)
        {
            TeamPortalService TPsvc = new TeamPortalService();

            ToolingRequestModel modelDetails = TPsvc.GetToolingRequestDetails(requestID);

            RequestID = requestID;
            Requester = modelDetails.Requester;
            RequestDate = modelDetails.RequestDate;
            Summary = modelDetails.Summary;
            Sources = slSources;
            Source = modelDetails.Source;
            Description = modelDetails.Description;
            Assignees = slAssignees;
            AssignedTo = modelDetails.AssignedTo;
            DefectTypes = slDefectTypes;
            DefectType = modelDetails.DefectType;
            Status = modelDetails.Status;
            Model = modelDetails.Model;
            CompletionDate = modelDetails.CompletionDate;

            Notes = TPsvc.GetToolingRequestNotes(requestID);


        }
    }

    public class ToolingNoteModel
    {
        public int ToolingNoteID { get; set; }
        public int RequestID { get; set; }
        public string NoteText { get; set; }
        public string NoteAuthor { get; set; }
        public DateTime NoteCreateDate { get; set; }
    }
}

To simplify things we can just look at the controller and view for adding a new request, however the model binding is also failing when trying to update requests. Fairly straight forward input form, it does have a check to verify we have records of the model input and no records are found the get controller method is recalled with an error message.

Controller:

        [ValidateInput(false)]
    public virtual ActionResult ToolingRequestAdd(bool? requestAdd, bool? badModel)
    {
        ToolingRequestModel model = new ToolingRequestModel();

        if (requestAdd == true)
        {
            ViewBag.RequestAdd = true;
        }
        else
        {
            ViewBag.RequestAdd = false;
        }

        if (badModel == true)
        {
            ViewBag.BadModel = true;
        }
        else
        {
            ViewBag.BadModel = false;
        }

        return View(model);
    }

    [HttpPost]
    [ValidateInput(false)]
    public virtual ActionResult ToolingRequestAdd(ToolingRequestModel model)
    {
        TeamPortalService TPsvc = new TeamPortalService();
        GP10Service gp10svc = new GP10Service();

        model.Requester = User.Identity.Name;
        model.RequestDate = DateTime.Now;
        model.Status = "New";

        if (gp10svc.CheckModelCode(model.Model))
        {
            return RedirectToAction("ToolingRequestAdd", new
            {
                requestAdd = false,
                badModel = true
            });
        }

        TPsvc.SaveToolingRequest(model);

        return RedirectToAction("ToolingRequestAdd", new
        {
            requestAdd = true
        });
    }

Here is the view:

    <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/FixedColumn.Master" Inherits="System.Web.Mvc.ViewPage<TeamPortal.Models.ToolingRequestModel>" %>

<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>

<asp:Content ID="Content4" ContentPlaceHolderID="LeftContent" runat="server">
        <% Html.RenderPartial("LocalNavToolingRequests"); %>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="RightContent" runat="server">

<div class="box">
        <!-- box / title -->
        <div class="title">
            <h5>
                Add Tooling Request</h5>
        </div>
        <%if (ViewBag.RequestAdd)
          { %>
              <div class="messages"><div id="successes0" class="message message-success"><div class="image"><img height="32" src="/Content/images/icons/success.png"></div><div class="text"><h6>Success</h6><span>Tooling Request Saved Successfully.</span></div><div class="dismiss"><a href="#successes0"></a></div></div></div>
        <%  } %>
        <%if (ViewBag.BadModel)
          { %>
              <div class="messages"><div id="errors0" class="message message-error"><div class="image"><img height="32" src="/Content/images/icons/error.png"></div><div class="text"><h6>Error</h6><span>The Spa Model Code Entered Was Invalid.  Verify The Model Is Still Active And The 3 Digits Are Correct.</span></div><div class="dismiss"><a href="#errors0"></a></div></div></div>
        <%  } %>

        <% using (Html.BeginForm())
           { %>

           <div class="form">
            <div class="fields">

            <div class="field  field-first">
                <div class="label">
                    <label>Request Summary:</label>
                </div>
                <div class="textbox">
                    <%: Html.TextBoxFor(m=>m.Summary) %>
                </div>
            </div>

            <div class="field">
                <div class="label">
                    <label>Model:</label>
                </div>
                <div class="textbox">
                    <%: Html.TextBoxFor(m=>m.Model) %>
                </div>
            </div>

            <div class="field">
                <div class="label">
                    <label>Source:</label>
                </div>
                <div class="select">
                    <%: Html.DropDownListFor(m => m.Source, Model.Sources)%>
                </div>
            </div>

            <div class="field">
                <div class="label">
                    <label>Defect Type:</label>
                </div>
                <div class="select">
                    <%: Html.DropDownListFor(m => m.DefectType, Model.DefectTypes)%>
                </div>
            </div>

            <div class="field">
                <div class="label">
                    <label>Description:</label>
                </div>
                <div class="textarea textarea-editor">
                    <%: Html.TextAreaFor(m => m.Description, 12, 70, new { @class = "tinymce-simple" })%>
                </div>
            </div>

            <% if (HttpContext.Current.User.IsInRole("Tooling"))
               { %>
            <div class="field">
                <div class="label">
                    <label>Assign Request To:</label>
                </div>
                <div class="select">
                    <%: Html.DropDownListFor(m => m.AssignedTo, Model.Assignees)%>
                </div>
            </div>
            <%} %>

            </div>
           </div>
           <input id="submit" name="submit" type="submit" value="Submit Request" />
       <%} %>

</div>

</asp:Content>

As a workaround I can use FormCollection to recreate a new model and fill the needed properties before saving or redirecting, however I'd really like to know where I've gone wrong here (plus FormCollection is a pain).

Very strange that this fails where the maintenance requests work fine. I thought copying my previous process would save me time...

Hopefully this question isn't "too localized", seems like it might be depending on the solution, I can re-post this elsewhere if that's the case.

Any help would be great, let me know if I've included enough (or too much) code/information. Thanks!

Upvotes: 2

Views: 3057

Answers (1)

J.T. Taylor
J.T. Taylor

Reputation: 4277

The default model binder will fail to bind (return null) when it cannot resolve which controller parameter to bind to.

From your View code, it looks like you must also have a model property called Model. I would say this is colliding in the default model binder with your argument name model in the ToolingRequestAdd HttpPost controller.

If you change the parameter name from model to something else like toolingRequestModel, I would guess this would fix the problem.

For example:

[HttpPost]
[ValidateInput(false)]
public virtual ActionResult ToolingRequestAdd(ToolingRequestModel toolingRequestModel)
{
    TeamPortalService TPsvc = new TeamPortalService();
    GP10Service gp10svc = new GP10Service();

    toolingRequestModel.Requester = User.Identity.Name;
                         .
                         .
                         .

Upvotes: 2

Related Questions