Asp.Net MVC 3(Razor,Json,Ajax)Master Detail - 详细说明保存失败

时间:2011-12-09 19:40:58

标签: asp.net-mvc-3

我是MVC3的新手并尝试构建一个简单的Invoicing应用程序。我的代码的问题是Ajax Post失败了,我无法找到原因。逐步浏览JQuery代码似乎很好但是当POST命中控制器时,Model.IsValid为false。问题似乎与儿童记录有关。发票主记录正在保存到数据库,但InvoiceRow不存在。问题在于SaveInvoice()函数。

 public class Invoice
{
    [Key]
    public int InvoiceID { get; set; }

    public int ContractID { get; set; }

    [Required]
    [Display(Name = "Invoice Date")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
    public DateTime InvoiceDate { get; set; }

    [Required]
    [Display(Name = "Invoice No")]
    public int InvoiceNumber { get; set; }

    [Required(AllowEmptyStrings = true)]
    [Display(Name = "Payment Date")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
    public DateTime PaymentDate { get; set; }

    public virtual Contract Contract { get; set; }

    public virtual ICollection<InvoiceRow> InvoiceRows { get; set; }
}

public class InvoiceRow
{
    [Key]
    public int Id { get; set; }
    public int InvoiceID { get; set; }
    public string RowDetail { get; set; }
    public int RowQty { get; set; }
    public decimal ItemPrice { get; set; }
    public decimal RowTotal { get; set; }

    public virtual Invoice Invoice { get; set; }
}


public class InvoiceController : Controller
{
    private CyberneticsContext db = new CyberneticsContext();

    //
    // GET: /Invoice/

    public ViewResult Index()
    {
        var invoices = db.Invoices.Include(i => i.Contract);
        return View(invoices.ToList());
    }

    //
    // GET: /Invoice/Details/5

    public ViewResult Details(int id)
    {
        Invoice invoice = db.Invoices.Find(id);
        return View(invoice);
    }

    //
    // GET: /Invoice/Create

    public ActionResult Create()
    {
        ViewBag.Title = "Create";
        ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName");
        return View();
    }

    //
    // POST: /Invoice/Create

    [HttpPost]
    public JsonResult Create(Invoice invoice)
    {
        try
        {
            if (ModelState.IsValid)
            {
                if (invoice.InvoiceID > 0)
                {
                    var invoiceRows = db.InvoiceRows.Where(ir => ir.InvoiceID == invoice.InvoiceID);

                    foreach (InvoiceRow row in invoiceRows)
                    {
                        db.InvoiceRows.Remove(row);
                    }

                    foreach (InvoiceRow row in invoice.InvoiceRows)
                    {
                        db.InvoiceRows.Add(row);
                    }

                    db.Entry(invoice).State = EntityState.Modified;
                }
                else
                {
                    db.Invoices.Add(invoice);
                }

                db.SaveChanges();

                return Json(new { Success = 1, InvoiceID = invoice.InvoiceID, ex = "" });
            }

        }
        catch (Exception ex)
        {
            return Json(new { Success = 0, ex = ex.Message.ToString() });
        }

        return Json(new { Success = 0, ex = new Exception("Unable to Save Invoice").Message.ToString() });
    }

    //
    // GET: /Invoice/Edit/5

    public ActionResult Edit(int id)
    {
        ViewBag.Title = "Edit";
        Invoice invoice = db.Invoices.Find(id);
        ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName", invoice.ContractID);
        return View("Create", invoice);
    }

    //
    // POST: /Invoice/Edit/5

    [HttpPost]
    public ActionResult Edit(Invoice invoice)
    {
        if (ModelState.IsValid)
        {
            db.Entry(invoice).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName", invoice.ContractID);
        return View(invoice);
    }

    //
    // GET: /Invoice/Delete/5

    public ActionResult Delete(int id)
    {
        Invoice invoice = db.Invoices.Find(id);
        return View(invoice);
    }

    //
    // POST: /Invoice/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {
        Invoice invoice = db.Invoices.Find(id);
        db.Invoices.Remove(invoice);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
  }
}

@model Cybernetics2012.Models.Invoice

... script tags excluded for brevity

<h2 class="h2">@ViewBag.Title</h2>


<script type="text/javascript">

$( document ).ready( function ()
{
    // here i have used datatables.js (jQuery Data Table)
    $( '.tableItems' ).dataTable
    ( 
        {
            "sDom": 'T<"clear">lfrtip',
            "oTableTools": { "aButtons": [], "sRowSelect": "single" },
            "bLengthChange": false,
            "bFilter": false,
            "bSort": true,
            "bInfo": false
        }
    );

    // Add DatePicker widget to InvoiceDate textbox
    $( '#InvoiceDate' ).datepicker();

    // Add DatePicker widget to PaymentDate textbox
    $( '#PaymentDate' ).datepicker();

    // Get the tableItems table
    var oTable = $( '.tableItems' ).dataTable();
} );


// this function is used to add item to table
function AddInvoiceItem()
{
    // Adding item to table
    $( '.tableItems' ).dataTable().fnAddData( [$( '#RowDetail' ).val(), $( '#RowQty' ).val(), $( '#ItemPrice' ).val(), $( '#RowQty' ).val() * $( '#ItemPrice' ).val()] );

    // clear text boes after adding data to table..
    $( '#RowDetail' ).val( "" )
    $( '#RowQty' ).val( "" )
    $( '#ItemPrice' ).val( "" )

}


// This function is used to delete selected row from Invoice Rows Table and then set deleted item to Edit text Boxes
function DeleteRow()
{
    // DataTables.TableTools plugin for getting selected row items
    var oTT = TableTools.fnGetInstance( 'tableItems' ); // Get Table instance
    var sRow = oTT.fnGetSelected(); // Get Selected Item From Table

    // Set deleted row item to editable text boxes
    $( '#RowDetail' ).val( $.trim( sRow[0].cells[0].innerHTML.toString() ) );
    $( '#RowQty' ).val( jQuery.trim( sRow[0].cells[1].innerHTML.toString() ) );
    $( '#ItemPrice' ).val( $.trim( sRow[0].cells[2].innerHTML.toString() ) );

    $( '.tableItems' ).dataTable().fnDeleteRow( sRow[0] );
}


//This function is used for sending data(JSON Data) to the Invoice Controller
function SaveInvoice()
{
    // Step 1: Read View Data and Create JSON Object

    // Creating invoicRow Json Object
    var invoiceRow = { "InvoiceID": "", "RowDetail": "", "RowQty": "", "ItemPrice": "", "RowTotal": "" };

    // Creating invoice Json Object
    var invoice = { "InvoiceID": "", "ContractID": "", "InvoiceDate": "", "InvoiceNumber": "", "PaymentDate": "", "InvoiceRows":[] };

    // Set Invoice Value
    invoice.InvoiceID = $( "#InvoiceID" ).val();
    invoice.ContractID = $( "#ContractID" ).val();
    invoice.InvoiceDate = $( "#InvoiceDate" ).val();
    invoice.InvoiceNumber = $( "#InvoiceNumber" ).val();
    invoice.PaymentDate = $( "#PaymentDate" ).val();

    // Getting Table Data from where we will fetch Invoice Rows Record
    var oTable = $( '.tableItems' ).dataTable().fnGetData();

    for ( var i = 0; i < oTable.length; i++ )
    {

        // IF This view is for edit then it will read InvoiceId from Hidden field
        if ( $( 'h2' ).text() == "Edit" )
        {
            invoiceRow.InvoiceID = $( '#InvoiceID' ).val();
        }
        else
        {
            invoiceRow.InvoiceID = 0;
        }

        // Set InvoiceRow individual Value
        invoiceRow.RowDetail = oTable[i][0];
        invoiceRow.RowQty = oTable[i][1];
        invoiceRow.ItemPrice = oTable[i][2];
        invoiceRow.RowTotal = oTable[i][3];           

        // adding to Invoice.InvoiceRow List Item
        invoice.InvoiceRows.push( invoiceRow );

        invoiceRow = { "RowDetail": "", "RowQty": "", "ItemPrice": "", "RowTotal": "" };
    }
    // Step 1: Ends Here


    // Set 2: Ajax Post
    // Here i have used ajax post for saving/updating information
    $.ajax( {
        url: '/Invoice/Create',
        data: JSON.stringify( invoice ),
        type: 'POST',
        contentType: 'application/json;',
        dataType: 'json',
        success: function ( result )
        {
            if ( result.Success == "1" )
            {
                window.location.href = "/Invoice/Index";
            }
            else
            {
                alert( result.ex );
            }
        }
    } );
}

</script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"    type="text/javascript"></script>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
    <legend>Invoice</legend>

    @if (Model != null)
    {
        <input type="hidden" id="InvoiceID" name="InvoiceID" value="@Model.InvoiceID" />
    }


    <div class="editor-label">
        @Html.LabelFor(model => model.ContractID, "Contract")
    </div>
    <div class="editor-field">
        @Html.DropDownList("ContractID", String.Empty)
        @Html.ValidationMessageFor(model => model.ContractID)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.InvoiceDate)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.InvoiceDate)
        @Html.ValidationMessageFor(model => model.InvoiceDate)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.InvoiceNumber)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.InvoiceNumber)
        @Html.ValidationMessageFor(model => model.InvoiceNumber)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.PaymentDate)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.PaymentDate)
        @Html.ValidationMessageFor(model => model.PaymentDate)
    </div>
</fieldset>

<br />

<fieldset>
    <legend>Add Invoice Row</legend>
    <br />
    <label>
        Row Detail :</label>
    @Html.TextBox("RowDetail")
    <label>
        Row Qty :</label>
    @Html.TextBox("RowQty", null, new { style = "width:20px;text-align:center" })
    <label>
        Item Price :</label>
    @Html.TextBox("ItemPrice", null, new { style = "width:70px" })

    <input onclick="AddInvoiceItem()" type="button" value="Add Invoice Item" />
    <table id="tableItems" class="tableItems" width="400px">
        <thead>
            <tr>
                <th>
                    Detail
                </th>
                <th>
                    Qty
                </th>
                <th>
                    Price
                </th>
                <th>
                    Row Total
                </th>
            </tr>
        </thead>
        <tbody>
            @if (Model != null)
            {
                foreach (var item in Model.InvoiceRows)
                {
                <tr>
                    <td>
                        @Html.DisplayFor(i => item.RowDetail)
                    </td>
                    <td>
                        @Html.DisplayFor(i => item.RowQty)
                    </td>
                    <td>
                        @Html.DisplayFor(i => item.ItemPrice)
                    </td>
                    <td>
                        @Html.DisplayFor(i => item.RowTotal)
                    </td>
                </tr>                       
                }
            }
        </tbody>
    </table>
    <br />
    <input onclick="DeleteRow()" type="button" value="Delete Selected Row" />
</fieldset>      

<p>
    <input onclick="SaveInvoice()" type="submit" value="Save Invoice" />
</p>

}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

1 个答案:

答案 0 :(得分:0)

您需要检查模型并提取错误。有了这些,您就可以开始解决导致模型绑定器失败的问题。

这是我用来将无效模型错误转储到输出控制台的扩展方法

http://pastebin.com/S0gM3vqg