views:

1319

answers:

1

Hi,

I've seen articles and blog posts indicating what I am attempting to do should be supported; however, I have been unable to get it working so hopefully someone can confirm that it does/does not work and/or tell me what I am doing wrong.

I have a "complex" model (Invoice containing a List of InvoiceItem) that I am attempting to bind to and I would only like to update certain attributes and certain items/indexes on the List objects). I believe my issue is primarily with whether or not the include/exclude works for List data. I am attempting to use UpdateModel() with its includeProperties, like so:

Public Function Edit(ByVal id As String, ByVal values As FormCollection)
    Dim invoice As Invoice = _getInvoice(id)
    UpdateModel(invoice, "", New String() {"InvoiceDate", "CustomerName", "Items[0].Price"})        
    'Do some stuff...
End Function

All of my code is below...

I have the following model:

Public Class Invoice
    Private _invoiceNumber As String
    Private _invoiceDate As DateTime
    Private _customerName As String
    Private _items As List(Of InvoiceItem)
    Public Property InvoiceNumber() As String
        Get
            Return _invoiceNumber
        End Get
        Set(ByVal value As String)
            _invoiceNumber = value
        End Set
    End Property
    Public Property InvoiceDate() As DateTime
        Get
            Return _invoiceDate
        End Get
        Set(ByVal value As DateTime)
            _invoiceDate = value
        End Set
    End Property
    Public Property CustomerName() As String
        Get
            Return _customerName
        End Get
        Set(ByVal value As String)
            _customerName = value
        End Set
    End Property
    Public Property Items() As List(Of InvoiceItem)
        Get
            Return _items
        End Get
        Set(ByVal value As List(Of InvoiceItem))
            _items = value
        End Set
    End Property
End Class

Public Class InvoiceItem
    Private _itemNumber As Integer
    Private _description As String
    Private _price As Decimal
    Public Property ItemNumber() As Integer
        Get
            Return _itemNumber
        End Get
        Set(ByVal value As Integer)
            _itemNumber = value
        End Set
    End Property
    Public Property Description() As String
        Get
            Return _description
        End Get
        Set(ByVal value As String)
            _description = value
        End Set
    End Property
    Public Property Price() As Decimal
        Get
            Return _price
        End Get
        Set(ByVal value As Decimal)
            _price = value
        End Set
    End Property
End Class

I have the following View:

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
    Inherits="System.Web.Mvc.ViewPage(Of MvcApplication2.Invoice)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <title>Edit</title>
</asp:Content>

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

    <h2>Edit</h2>

    <%=Html.ValidationSummary()%>

    <% Using Html.BeginForm() %>

        <fieldset>
            <legend>Fields</legend>
            <p>
                <label for="InvoiceNumber">InvoiceNumber:</label>
                <%= Html.TextBox("InvoiceNumber") %>
                <%= Html.ValidationMessage("InvoiceNumber", "*") %>
            </p>
            <p>
                <label for="InvoiceDate">InvoiceDate:</label>
                <%= Html.TextBox("InvoiceDate") %>
                <%= Html.ValidationMessage("InvoiceDate", "*") %>
            </p>
            <p>
                <label for="CustomerName">CustomerName:</label>
                <%= Html.TextBox("CustomerName") %>
                <%= Html.ValidationMessage("CustomerName", "*") %>
            </p>
            <%For x As Integer = 0 To ViewData.Model.Items.Count - 1%>       
            <p>    
                <label for="Item<%=ViewData.Model.Items(x).ItemNumber %>">Item <%=ViewData.Model.Items(x).ItemNumber%>:</label>
                <%=Html.Hidden("Items.Index", x)%>
                <%=Html.TextBox(String.Format("Items[{0}].Description", x), ViewData.Model.Items(x).Description)%>
                <%=Html.ValidationMessage("Description", "*")%>
                <%=Html.TextBox(String.Format("Items[{0}].Price", x), ViewData.Model.Items(x).Price)%>
                <%=Html.ValidationMessage("Price", "*")%>
            </p>
            <%Next x%>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>

    <% End Using %>


</asp:Content>

And here is my Controller:

Public Class InvoicingController
    Inherits System.Web.Mvc.Controller

    Private Function _getInvoice(ByVal id As String) As Invoice
        Dim invoice As New Invoice
        invoice.InvoiceNumber = id
        invoice.CustomerName = "John Doe"
        invoice.InvoiceDate = DateTime.Now
        invoice.Items = New List(Of InvoiceItem)
        For x As Integer = 1 To 5
            Dim item As InvoiceItem = New InvoiceItem
            item.ItemNumber = x
            item.Description = String.Format("Item {0}", x)
            item.Price = 100 + x
            invoice.Items.Add(item)
        Next
        Return invoice
    End Function

    <AcceptVerbs(HttpVerbs.Post)> _
    Function Edit(ByVal id As String) As ActionResult
        Dim invoice As Invoice = _getInvoice(id)
        Return View(invoice)
    End Function



        'UpdateModel(invoice, "", New String() {"InvoiceDate", "CustomerName", "Items"}) 'NOTE: THis line appears to update any InvoiceItem attributes that are supplied
        UpdateModel(invoice, "", New String() {"InvoiceDate", "CustomerName", "Items[0].Price"}) 'This line does not allow the first item's Price to be updated.

        Return View()

    End Function

End Class

Thanks in advance!

A: 

@j0tt

tlangdon here...allow me to hopefully be more clear: I CAN get include and exclude overloads working (for example, I can tell it to include CustomerName and exclude InvoiceDate and that works fine. My issue appears to be with include/exclude of the List-based fields only - for example Items[0].Price. So, how can I include Price from the first Item only (meaning Items[0].Description is excluded and, further, Items[1], Items[2], etc. are excluded, as well)?

Thanks,tlangdon

tlangdon
Sorry I misunderstood your question, It seems like you would need to handle that more manually. What blog posts made that seem to be supported?
j0tt
for starters...http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
tlangdon
Hey. I was wondering if this was over thinking? Can't you just conditionally write out the Items[0].Price as a Span instead of a TextBox. It seems like your desired behavior is read only couldn't you also control that in the view?Additionally on the backend you could implement a ValueProvider that did not return Item[0].Price from the Request so as not to allow that on the server side. Just brainstorming with you. Hope you already solved this issue.
j0tt