2014-02-10 197 views
15

我試圖更新舊的Web窗體應用程序以使用4.5中添加的新模型綁定功能,類似於MVC綁定功能。ASP.NET Web Forms 4.5模型綁定模型包含集合

我在製作可編輯的FormView時遇到了麻煩,它呈現的是包含簡單成員和包含其他模型集合的成員的單個模型。我需要用戶能夠編輯父對象的簡單屬性和子集合的屬性。

問題是在代碼嘗試更新模型時,模型綁定後子集合(ProductChoice.Extras)始終爲空。

這裏是我的模型:

[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; } 
} 

背後我的用戶控件代碼:

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) 
     { 
     } 
    } 
} 

我的控制標記:

<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> 

我試圖使Extras屬性BindingList而不是List,但它沒有馬如有任何差異,Extras集合未在UpdateProduct方法中綁定。

+0

你不能嘗試只傳入模型ProductChoice。 EG: 公共無效的UpdateProduct(ProductChoice模型) {} 好 – izip

+0

問題@PaulD:我原以爲這FormView控件/中繼器的做法是很常見的(我的做法是相同的你) - 但我沒有也能夠破解這一個。 – Merenzo

+0

我試圖實現一個自定義['System.Web.ModelBinding.IValueProvider'](http://msdn.microsoft.com/en-us/library/system.web.modelbinding.ivalueprovider(v = vs.110).aspx ),爲了對TryUpdateModel()使用[overload](http://msdn.microsoft.com/en-us/library/hh140347(v = vs.110).aspx),但是我的'IValueProvider'實現陷入了一個無限遞歸循環,因爲它反映了它通過對象屬性的方式...... – Merenzo

回答

-2

傳遞查詢字符串中的額外值,在update方法中添加參數值。在HTML 5使用數據屬性的3個值存儲在窗體視圖元素

例如

UpdateMethod="UpdateProduct/104/coke/2" 

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

你必須寫在路由配置的路由規則的第一種方法。

裏面象如下

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

我看不出這將一個未知的長工作收集,根據他的問題?注意他描述問題的代碼註釋:'它應該包含兩個ProductChoiceExtra對象'。 – Merenzo

+1

正如@Merenzo所說,我不知道在編譯時有多少額外產品可用於某個產品 - 可能不存在或可能存在23. – PaulD

+0

減1這個答案在綁定可變數量的集合時沒有意義值。 – 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

用戶控制不幸的是,我不知道究竟如何,所以我不能確定如何使用中繼器重現此,這是與Web窗體進行,但在MVC的模型聯編程序需要一個索引來重建列表。如果我不得不猜測這是如何在Web表單完成,這將是類似這樣:

<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> 

通知我替換爲中繼for循環,通過與訪問每個索引集合迭代額外。這與我需要在ASP.NET MVC中執行所需操作的方式類似。當提交表單時允許模型聯編程序重新構建對象的有序列表,索引隨Web表單的其餘部分一起發佈。

我希望這是一些幫助和原諒我的任何錯誤,因爲我沒有一個web表單項目來測試此刻。

-1

您應該在內部數據列表中指定編輯項目模板,因爲項目模板中的屬性可能不會在自動構建並傳遞給Update方法的模型中返回。 我沒有時間嘗試,但這應該工作...

<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

中繼器沒有EditItemTemplate。 – naasking

+0

將Repeater更改爲Datalist。還沒有測試過。抱歉 –

0

挖掘到System.Web.ModelBinding揭示了CollectionModelBinder預計到FormValueProvider傳遞的值將是相同的格式,因爲它們將是MVC,那就是:MyCollection的[I]

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

不幸的是,您的中繼器的元素名稱將不符合該標準。

雖然肯定非正統的,你仍然可以通過編寫非服務器文本框,然後給他們一個名字開始與DataList控件命名容器,隨後該指數實現這一目標。還要感謝「Request.Unvalidated」(也在4.5中介紹),即使它不是由服務器端控件表示的,也可以將數據綁定到該數據。