2014-02-10 150 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中介绍),即使它不是由服务器端控件表示的,也可以将数据绑定到该数据。