2014-02-10 8 views
15

Ich versuche, eine alte Web Forms-Anwendung zu aktualisieren, um die in 4.5 hinzugefügten neuen Modellbindungsfunktionen zu verwenden, ähnlich den MVC-Bindungsfeatures.Bindung von ASP.NET Web Forms 4.5-Modellen, in der das Modell eine Auflistung enthält

Ich habe Probleme beim Erstellen eines bearbeitbaren FormView, das ein einzelnes Modell darstellt, das einfache Member sowie ein Member enthält, das eine Sammlung anderer Modelle darstellt. Ich brauche den Benutzer, um die einfachen Eigenschaften des übergeordneten Objekts und die Eigenschaften der untergeordneten Sammlung bearbeiten zu können.

Das Problem besteht darin, dass die untergeordnete Auflistung (ProductChoice.Extras) immer Null nach Modellbindung ist, wenn der Code versucht, das Modell zu aktualisieren.

Hier sind meine Modelle:

[Serializable] 
public class ProductChoice 
{ 
    public ProductChoice() 
    { 
     Extras = new List<ProductChoiceExtra>(); 
    } 

    public int Quantity { get; set; } 
    public int ProductId { get; set; } 
    public List<ProductChoiceExtra> Extras { get; set; } 
} 

[Serializable] 
public class ProductChoiceExtra 
{ 
    public int ExtraProductId { get; set; } 
    public string ExtraName { get; set; } 
    public int ExtraQuantity { get; set; } 
} 

Und hinter meinem Benutzersteuercode:

public partial class ProductDetails : System.Web.UI.UserControl 
{ 
    private Models.ProductChoice _productChoice; 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     _productChoice = new Models.ProductChoice() 
     { 
      Quantity = 1, 
      ProductId = 1 
     }; 
     _productChoice.Extras.Add(new Models.ProductChoiceExtra() 
     { 
      ExtraProductId = 101, 
      ExtraName = "coke", 
      ExtraQuantity = 1 
     }); 
     _productChoice.Extras.Add(new Models.ProductChoiceExtra() 
     { 
      ExtraProductId = 104, 
      ExtraName = "sprite", 
      ExtraQuantity = 2 
     }); 

    } 

    public Models.ProductChoice GetProduct() 
    { 
     return _productChoice; 
    } 

    public void UpdateProduct(Models.ProductChoice model) 
    { 
     /* model.Extras is always null here, it should contain two ProductChoiceExtra objects */ 

     if (TryUpdateModel(_productChoice) == true) 
     { 
     } 
    } 
} 

Meine Steuer Markup:

<div id="selectOptions"> 
    <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" 
     ItemType="Models.ProductChoice" 
     SelectMethod="GetProduct" 
     UpdateMethod="UpdateProduct" > 

     <EditItemTemplate> 
      <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> 
      <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> 
      <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> 

      <asp:Repeater ID="Extras" ItemType="Models.ProductChoiceExtra" DataSource="<%# BindItem.Extras %>" runat="server"> 
       <ItemTemplate> 
        <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> 
        <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> 
        <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="Quantity" runat="server" /> 
       </ItemTemplate> 
      </asp:Repeater> 
     </EditItemTemplate> 
    </asp:FormView> 
</div> 

ich die Extras Eigenschaft ein BindingList machen versucht haben, eher als ein List aber es war nicht ma ke Unterschied, die Extras Sammlung ist nicht in der UpdateProduct Methode gebunden.

+0

Können Sie nicht einfach das Modell ProductChoice übergeben. Beispiel: public void UpdateProduct (ProductChoice Modell) { } – izip

+0

Gute Frage @PaulD: Ich hatte gedacht, dass diese Formview/Repeater Ansatz ein allgemeines wäre (mein Ansatz ist es, Ihnen identisch) - aber ich habe nicht war in der Lage, diesen zu knacken. – Merenzo

+0

Ich habe versucht, eine benutzerdefinierte ['System.Web.ModelBinding.IValueProvider '] (http://msdn.microsoft.com/en-us/library/system.web.modelbinding.ivalueprovider (v = vs.110) .aspx zu implementieren), um die [Überladung] (http://msdn.microsoft.com/en-us/library/hh140347 (v = vs.110) .aspx) für 'TryUpdateModel()', aber meinen 'IValueProvider' zu verwenden Implementierung wurde in einer unendlichen rekursiven Schleife festgefahren, als sie ihren Weg durch die Objekteigenschaften widerspiegelte ... – Merenzo

Antwort

-2

Übergeben Sie die Extraswerte in Abfragezeichenfolge, fügen Sie Parameterwerte in der Aktualisierungsmethode hinzu. Verwenden Datenattribute auf HTML 5, diese drei Werte in der Formularansicht Elemente zu speichern

Beispiel

UpdateMethod="UpdateProduct/104/coke/2" 

oder

UpdateMethod="UpdateProduct/?ExtraProductId=104&ExtraName=coke&ExtraQuantity=2" 

für den ersten Ansatz, den Sie in Route Config Routing-Regel zu schreiben.

Innerhalb der Benutzersteuerung wie wie unten

public void UpdateProduct(Models.ProductChoice model, int ExtraProductId, string ExtraName, int ExtraQuantity) 
{ 
    /* model.Extras is always null here, it should contain two ProductChoiceExtra objects */ 

    if (TryUpdateModel(_productChoice) == true) 
    { 
     model.Extras.Add(new Models.ProductChoiceExtra() 
     { 
     ExtraProductId = ExtraProductId, 
     ExtraName = ExtraName, 
     ExtraQuantity = ExtraQuantity 
     }); 
    } 
} 
+3

Ich sehe nicht, wie dies für eine unbekannte Länge funktionieren würde Sammlung, nach seiner Frage? Beachten Sie seine Code-Kommentare, in denen das Problem beschrieben wird: 'Es sollte zwei ProductChoiceExtra-Objekte enthalten. – Merenzo

+1

Wie @Merenzo sagt, weiß ich nicht, wie viele Extras für ein Produkt zur Kompilierzeit verfügbar sind - es könnte keine geben oder es könnte 23 sein. – PaulD

+0

Minus 1. Diese Antwort macht keinen Sinn, wenn eine Sammlung mit einer variablen Menge gebunden wird von Werten. – contactmatt

-1
[Serializable] 
public class ProductChoice 
{ 
public int Quantity { get; set; } 
    public int ProductId { get; set; } 
    public ProductChoiceExtra Extras { get; set; } 
} 

[Serializable] 
public class ProductChoiceExtra 
{ 
    public int ExtraProductId { get; set; } 
    public string ExtraName { get; set; } 
    public int ExtraQuantity { get; set; } 
    public List<ProductChoiceExtra> listProducts{get;set;} 
} 
0

Leider weiß ich nicht genau, wie diese mit Web Forms getan wird, damit ich nicht sicher bin, wie dies mit einem Repeater zu reproduzieren, sondern in MVC der Modellbinder benötigt einen Index, um die Liste zu rekonstruieren. Wenn ich habe zu erraten, wie dies in Web-Formularen geschehen ist, wäre es etwas ähnliches wie diesen:

<div id="selectOptions"> 
    <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" 
     ItemType="Models.ProductChoice" 
     SelectMethod="GetProduct" 
     UpdateMethod="UpdateProduct" > 
     <EditItemTemplate> 
      <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> 
      <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> 
      <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> 

      <% for (int i = 0; i < BindItem.Extras.Count; i++) 
      { %> 
        <asp:HiddenField Value="<%# BindItem.Extras[i].ExtraProductId %>" ID="ExtraProductId" runat="server" /> 
        <asp:Label Text="<%# BindItem.Extras[i].ExtraName %>" ID="Name" runat="server" /> 
        <asp:TextBox Text="<%# BindItem.Extras[i].ExtraQuantity %>" ID="Quantity" runat="server" /> 
      <% } %> 
     </EditItemTemplate> 
    </asp:FormView> 
</div> 

Hinweis ersetzt ich den Repeater mit einem for-Schleife, dass eine Iteration durch die Sammlung mit dem Index verwendet, um jeden Zugriff Extra. Dies ist ähnlich wie ich in ASP.NET MVC tun soll, was Sie wollen. Der Index wird zusammen mit dem Rest des Webformulars gepostet, wenn das Formular übergeben wird, wodurch der Modellbinder die geordnete Liste der Objekte rekonstruieren kann.

Ich hoffe, dies ist eine Menge Hilfe und verzeihen Sie mir für etwaige Fehler, da ich kein Web-Formular-Projekt habe, um dies mit im Moment zu testen.

-1

Sie sollten eine Elementvorlage bearbeiten in einem inneren Datenlistenelement angeben, da die Eigenschaften in der Elementvorlage möglicherweise nicht in dem Modell zurückgegeben werden, das automatisch erstellt und an die Update-Methode übergeben wird. Ich hatte keine Zeit, es zu versuchen, aber das sollte funktionieren ...

<div id="selectOptions"> 
    <asp:FormView runat="server" ID="fvProductSelection" DefaultMode="Edit" 
     ItemType="Models.ProductChoice" 
     SelectMethod="GetProduct" 
     UpdateMethod="UpdateProduct" > 

     <EditItemTemplate> 
      <asp:linkbutton id="UpdateButton" text="Update" commandname="Update" runat="server"/> 
      <asp:HiddenField runat="server" ID="ProductId" Value="<%# BindItem.ProductId %>" /> 
      <asp:TextBox Text ="<%# BindItem.Quantity %>" ID="Quantity" runat="server" /> 

      <asp:DataList ID="Extras" DataSource="<%# DataBinder.Eval(Container.DataItem, "Extras") %>" runat="server"> 
       <EditItemTemplate> 
        <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> 
        <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> 
        <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="TextBox1" runat="server" /> 
       </EditItemTemplate> 
       <ItemTemplate> 
        <asp:HiddenField Value="<%# BindItem.ExtraProductId %>" ID="ExtraProductId" runat="server" /> 
        <asp:Label Text="<%# BindItem.ExtraName %>" ID="Name" runat="server" /> 
        <asp:TextBox Text="<%# BindItem.ExtraQuantity %>" ID="Quantity" runat="server" /> 
       </ItemTemplate> 
      </asp:Repeater> 
     </EditItemTemplate> 
    </asp:FormView> 
</div> 
+0

Repeater hat kein EditItemTemplate. – naasking

+0

Repeater zu Datalist geändert. Habe noch nicht getestet. Es tut uns leid –

0

Digging in System.Web.ModelBinding zeigt, dass der CollectionModelBinder erwartet, dass in dem FormValueProvider gebenen Werte im gleichen Format sein würden, wie sie für MVC wären, das heißt: MyCollection [i]

public static string CreateIndexModelName(string parentName, string index) 
{ 
    if (parentName.Length != 0) 
    { 
     return (parentName + "[" + index + "]"); 
    } 
    return ("[" + index + "]"); 
} 

Leider entsprechen die Elementnamen Ihres Repeaters nicht diesen Kriterien.

Während sicherlich unorthodox, könnten Sie dies erreichen, indem Sie Nicht-Server-Textfelder schreiben, und geben Sie ihnen dann einen Namen beginnend mit Ihrem Datenbenennungscontainer, gefolgt von dem Index. Und dank "Request.Unvalidated" (ebenfalls in 4.5 eingeführt) haben Sie die Möglichkeit, Daten in diese Daten zu übernehmen, obwohl sie nicht durch serverseitige Steuerelemente repräsentiert werden.

Verwandte Themen