简体   繁体   中英

View returns NULL to controller Action (Model not binding)

I've been struggeling with this one for quite a while. Everything goes as planned until I try to return the values filled in from the View back to the Controller (See below):

填充后查看

Using Fiddler, as soon as I submit the data it shows valid html (Please see below:)

4个字段-有效

To my understanding, this should bind to the model. I am posting back to the Finance ActionResult, but this is what I am getting:

返回null

NULL is returned in ActionResult Finance

I just don't understand this. I am passing a List into the View :

@model List<FinanceMVC.Models.FinanceViewModel>

And my ActionResult is expecting the following:

List<FinanceViewModel> finance

I have a feeling I have a problem in my model somewhere, and I've tried my best to find it, but I just can't see it. Has anyone experienced this before? Please can someone lend a hand.

Please see my code below:

I have the following Model :

namespace FinanceMVC.Models
{
    //public class Finance
    //{

    //}

    public class FinanceViewModel
    {
        /*ID*/
        //[Required]
        //[Display(Name = "ID")]
        //public int ID { get; set; }

        /*BINL_BIND_ID*/
        [Required]
        [Display(Name = "Invoice Type")]
        public IEnumerable<SelectListItem> InvoiceType { get; set; }

        /*BINL_Inv_Num_Pointer*/
        [RegularExpression(@"^[0-9]*$",
                            ErrorMessage = "Email is not valid")]
        [Required]
        [Display(Name = "Invoice Number")]
        public string InvoiceNumber { get; set; }

        /*BINL_Inv_Num_Period*/
        [Required]
        [Display(Name = "Invoice Number Period")]
        public IEnumerable<SelectListItem> InvoiceNumberPeriod { get; set; }
        //public List<C_Period> InvoiceNumberPeriod { get; set; }

        /*BINL_Created*/
        [Display(Name = "Invoice Created Date")]
        [Required]
        [DataType(DataType.DateTime)]
        public DateTime InvoiceDateCreated { get; set; }

        ///*BINL_SystemInserted*/
        //[Required]
        //[Display(Name = "Invoice System Inserted")]
        //public bool InvoiceSystemInserted { get; set; }
    }

    public class C_InvoiceType
    {
        public int ID { get; set; }
        public string Description { get; set; }
    }

    public class C_Period
    {
        public int Period { get; set; }
    }

    public class ReturnInfo
    {
        string InvoiceType { get; set; }
        string InvoiceNumber { get; set; }
        string InvoiceNumberPeriod { get; set; }
        string InvoiceDateCreated { get; set; }
    }
}

View :

@model List<FinanceMVC.Models.FinanceViewModel>
@{
    ViewBag.Title = "Index";
}
@using (Html.BeginForm("Finance", "Finance", FormMethod.Post, new { @class = "form-horizontal" }))
{
    <div class="col-md-1 fade in" style="margin-left: 5%; width: 100%; font-size: 11px; height: auto;">
        <div id="div_Finance_Container" style="width: 100%; height: 80%; overflow: auto; text-align: left;" runat="server" class="scroll-pane">
            <div style="height: 500px; overflow: auto;">
                <table id="dataTable" border="0">
                    @for(int i = 0; i <Model.Count; i++)
                    {
                    <tr>
                        @Html.Label("The next recommended Invoice Number is: ", "The next recommended Invoice Number is: " + Model[i].InvoiceNumber, new { style="font-weight:bold;" })
                    </tr>

                    <br />
                    <tr class="tr_clone">
                        <td>
                            <div class="col-md-1" style="width: 20%;">
                                @Html.DropDownList("InvoiceType[" + @i + "].InvoiceType", Model[i].InvoiceType)
                            </div>
                        </td>
                        <td>
                            @Html.TextBox("InvoiceNumber[" + @i + "].InvoiceNumber", Model[i].InvoiceNumber)
                        </td>

                        <td>
                            @Html.DropDownList("InvoiceNumberPeriod[" + @i + "].InvoiceNumberPeriod", Model[i].InvoiceNumberPeriod, new { @class = "InvoiceNumberPeriod", string.Empty })
                        </td>
                        <td>
                            @Html.TextBox("InvoiceDateCreated[" + @i + "].InvoiceDateCreated", Model[i].InvoiceDateCreated, new { @class = "InvoiceDateCreated", @type = "date" })
                        </td>
                        <td>
                            <input type="button" name="add" value="Add another record" class="tr_clone_add">
                        </td>
                    </tr>
                    }

                </table>
                <input type="submit" value="Save Bulk Data" />
            </div>
        </div>
    </div>
}

and Controller :

public class FinanceController : Controller
{
    public ActionResult Finance()
    {
        List<C_Period> c_Per = new List<C_Period>();

        // This is only for show by default one row for insert data to the database
        List<FinanceViewModel> FinanceViewList = new List<FinanceViewModel> 
        { 
            new FinanceViewModel 
            { 
                /*ID = 0 ,*/ InvoiceType = ListInvoiceTypesForFinances() /*ReturnInvoiceType()*/, InvoiceNumber = ReturnInvoiceNumber(), InvoiceNumberPeriod = ListInvoiceNumPeriodForFinances() /*c_Per*/, InvoiceDateCreated = DateTime.Now, /*InvoiceSystemInserted = false*/
            }, 
            new FinanceViewModel 
            { 
                /*ID = 0 ,*/ InvoiceType = ListInvoiceTypesForFinances() /*ReturnInvoiceType()*/, InvoiceNumber = ReturnInvoiceNumber(), InvoiceNumberPeriod = ListInvoiceNumPeriodForFinances() /*c_Per*/, InvoiceDateCreated = DateTime.Now, /*InvoiceSystemInserted = false*/
            },
            new FinanceViewModel 
            { 
                /*ID = 0 ,*/ InvoiceType = ListInvoiceTypesForFinances() /*ReturnInvoiceType()*/, InvoiceNumber = ReturnInvoiceNumber(), InvoiceNumberPeriod = ListInvoiceNumPeriodForFinances() /*c_Per*/, InvoiceDateCreated = DateTime.Now, /*InvoiceSystemInserted = false*/
            } 
        };

        return View(FinanceViewList);
    }

    // 
    // GET: /Finance/ 

    /*Matches file name of Finance.cshtml*/
    //[HttpPost]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Finance(List<FinanceViewModel> finance)
    {
        if (ModelState.IsValid)
        {

        }

        return null;
    }

    // 
    // GET: /Finance/Welcome/ 

    //public string Index()
    //{
    //    return "This is the Welcome action method...";
    //}

    public static string ReturnInvoiceNumber()
    {
        string sQ = ""
                   + " Select Max(BINL_Inv_Num_Pointer) AS [Last Invoice Number]  \n"
                   + "   From Biller_InvoiceNum_List with(nolock) \n"
                   ;

        using (SqlConnection sCon = new SqlConnection(Application_Info.sMOB_Master_Conn()))
        {
            if (sCon.State != ConnectionState.Open)
            {
                sCon.Open();
            }

            using (SqlCommand sCmd = new SqlCommand(sQ, sCon))
            {
                using (SqlDataReader sRdr = sCmd.ExecuteReader())
                {
                    string LastInvoiceRecordNumber = "";
                    while (sRdr.Read())
                    {
                        LastInvoiceRecordNumber = (string)sRdr["Last Invoice Number"];
                    }

                    int LastInvoiceLength = LastInvoiceRecordNumber.Length;
                    int number = int.Parse(LastInvoiceRecordNumber);

                    /*Increment to get recommended next Invoice Number*/
                    number = (number + 1);

                    string IntStr = number.ToString();
                    string NextInvoiceRecordNumber = IntStr.PadLeft(LastInvoiceLength, '0');

                    return NextInvoiceRecordNumber;
                }
            }

        }
    }

    public static IEnumerable<C_InvoiceType> ReturnIType()
    {
        var srtQry = "\n"
                    + " Select ID, BIND_Description \n"
                    + " From Biller_InvoiceNum_DefSet with(nolock) \n"
                    ;

        using (var conn = new SqlConnection(Application_Info.sMOB_Master_Conn()))
        using (var objCommand = new SqlCommand(srtQry, conn) { CommandType = CommandType.Text })
        using (var dt = new DataTable())
        using (var adp = new SqlDataAdapter(objCommand))
        {
            conn.Open();
            adp.Fill(dt);
            return dt.AsEnumerable().Select(o => new C_InvoiceType
            {
                ID = o.Field<int>("ID"),
                Description = o.Field<string>("BIND_Description"),
            }).ToList();
        }
    }

    public static IEnumerable<SelectListItem> ListInvoiceTypesForFinances()
    {
        var listIVTypes = ReturnIType();

        return listIVTypes
                .Select(o => new SelectListItem
                {
                    Text = o.Description,
                    Value = o.ID.ToString()
                })
                .ToList();
    }

    public static IEnumerable<C_Period> ReturnINumPeriod()
    {
        var srtQry = "\n"
                    + " Select BINL_Inv_Num_Period \n"
                    + " From Biller_InvoiceNum_List with(nolock) \n"
                    + " Where ID = 99999 \n"
                    ;

        using (var conn = new SqlConnection(Application_Info.sMOB_Master_Conn()))
        using (var objCommand = new SqlCommand(srtQry, conn) { CommandType = CommandType.Text })
        using (var dt = new DataTable())
        using (var adp = new SqlDataAdapter(objCommand))
        {
            conn.Open();
            adp.Fill(dt);
            return dt.AsEnumerable().Select(o => new C_Period
            {
                Period = o.Field<int>("BINL_Inv_Num_Period"), 
            }).ToList();
        }
    }

    public static IEnumerable<SelectListItem> ListInvoiceNumPeriodForFinances()
    {
        var listIVTypes = ReturnINumPeriod();

        return listIVTypes
                .Select(o => new SelectListItem
                {
                    Text = o.Period.ToString(),
                    Value = o.Period.ToString()
                })
                .ToList();
    }
}

UPDATE:

So, changed View @Html helpers like below:

@for(int i = 0; i <Model.Count; i++)
{
<tr>
    @Html.Label("The next recommended Invoice Number is: ", "The next recommended Invoice Number is: " + Model[i].InvoiceNumber, new { style="font-weight:bold;" })
</tr>

<br />
<tr class="tr_clone">
    <td>
        <div class="col-md-1" style="width: 20%;">
            @*@Html.DropDownList("InvoiceType[" + @i + "].InvoiceType", Model[i].InvoiceType)*@
            @Html.DropDownListFor(x => Model[i].InvoiceType, Model[i].InvoiceType)
        </div>
    </td>
    <td>
        @*@Html.TextBox("InvoiceNumber[" + @i + "].InvoiceNumber", Model[i].InvoiceNumber)*@
        @Html.TextAreaFor(x => Model[i].InvoiceNumber)
    </td>

    <td>
        @*@Html.DropDownList("InvoiceNumberPeriod[" + @i + "].InvoiceNumberPeriod", Model[i].InvoiceNumberPeriod, new { @class = "InvoiceNumberPeriod", string.Empty })*@
        @Html.DropDownListFor(x => Model[i].InvoiceNumberPeriod, Model[i].InvoiceNumberPeriod, new { @class = "InvoiceNumberPeriod", string.Empty })
    </td>
    <td>
        @*@Html.TextBox("InvoiceDateCreated[" + @i + "].InvoiceDateCreated", Model[i].InvoiceDateCreated, new { @class = "InvoiceDateCreated", @type = "date" })*@
        @Html.TextBoxFor(x => Model[i].InvoiceDateCreated, new { @class = "InvoiceDateCreated", @type = "date" })
    </td>
    <td>
        <input type="button" name="add" value="Add another record" class="tr_clone_add">
    </td>
</tr>
}

Still having problems with drop down lists displaying:

从视图发布到控制器

If you use them, your prefixes need to match your action parameter's name:

@Html.TextBox("finance[" + i + "].InvoiceNumber", Model[i].InvoiceNumber)
@Html.DropDownList("finance[" + i + "].InvoiceNumberPeriod", Model[i].InvoiceNumberPeriod, new { @class = "InvoiceNumberPeriod", string.Empty })
@Html.TextBox("finance[" + i + "].InvoiceDateCreated", Model[i].InvoiceDateCreated, new { @class = "InvoiceDateCreated", @type = "date" })

They are not required though when you have only one parameter, in which case you can use:

@Html.TextBox("[" + i + "].Property", ...)

You also can, and actually should, use the For methods:

@Html.TextBoxFor(m => m[i].InvoiceNumber, ...)
@Html.DropDownListFor(m => m[i].InvoiceNumberPeriod, ...)
@Html.TextBoxFor(m => m[i].InvoiceDateCreated, ...)

You have to write it like this:

@Html.DropDownList("[" + @i + "].InvoiceType", Model[i].InvoiceType)
@Html.TextBox("[" + @i + "].InvoiceNumber", Model[i].InvoiceNumber)
.................................
.................................

Because you don't have InvoiceType collection property in your Model which has another property InvoiceType when you write InvoiceType[0].InvoiceType you are saying in my Model i have collection with name InvoiceType which has a Property name InvoiceType which is not the case

Better approach is to use For Helpers which will automatically bind control value with Model:

@Html.DropDownListFor(x=> Model[i].InvoiceType)
@Html.TextBoxFor (x=> Model[i].InvoiceNumber)
.................................
.................................

You can also refere this related SO post

OMG. Use the strongly typed helpers. None of your control names match your property names

@Html.TextBoxFor(m => m[i].InvoiceNumber)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM