ASP.Net MVC强类型部分视图和继承属性

3
我正在使用MS MVC 3构建一个Web应用程序,并遇到了一个问题,可能是由于我对模型绑定的理解有漏洞。
首先,我有一个相当标准的模型(为了简洁起见省略了无关的内容,更改了名称以保护无辜对象的隐私):
public class ModelBase
{
    public int Id { get; set; }
}

 public class Order : ModelBase
{
    public List<Product> Products { get; set; }
}

public class Product : ModelBase
{
    public int OrderId { get; set;}
}

为了显示和编辑这些内容,我有一个针对Order类强类型的视图,其中包含一个强类型的Partial View,该Partial View针对Product类。 Partial View的顶部如下所示:
@model Product

@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.OrderId)
//loads of actual editable properties

我将局部视图插入到主视图中,就像这样:
@Html.Partial("EditorTemplates/Product", Model.Products.First())

...当视图在浏览器中呈现时,“Id”隐藏输入包含订单的ID,而不是我期望和需要的产品的ID :(
我错过了什么?是否可以在不更改模型和视图结构的情况下修复它?

看起来还不错,你怎么确定 Id 就是订单呢?OrderId 显示正常吗? - Wil
1
OrderId没有正确显示,而且在Id字段中显示的是相同的值。虽然我不确定它是否来自Order,因为该值也作为请求URL的一部分存在(/Orders/Step2/10,其中10是订单ID)。这个值会不会覆盖Id字段呢? - Atrieos
您可以通过数据库确认orderid是否与此记录的id相同? - Wil
在我的特定测试案例中,订单编号是10,产品编号是3。我在两个字段中都得到了10。没有产品的编号是10。 - Atrieos
2个回答

3
更改我的测试项目后,我可以确认 /Home/Index?id=33 或者 /Home/Index/33 会覆盖模型值。你能将这个参数从URL中移除吗?

2
就是这样!绑定器以不区分大小写的方式将路由值放入字段中,并覆盖来自模型的值。我在Global.Asax中添加了一个新的路由,用于应用程序的订单编辑部分,如下所示:"Orders/{StepNo}/{OrderId}" ...现在我的值已经被保留,因为它不再将默认路由值{id}与我的"I d"字段匹配。只需要新路由和一些新的操作参数名称即可完成。无需进行结构更改,MVC也不会出现我最初担心的继承问题。非常感谢您的帮助 :) - Atrieos

1

你需要改变结构。

你可以将部分内容放入主视图中,或者将部分强类型化为订单而不是产品,或者使用Html.Hidden()构建隐藏字段。MVC在HiddenFor中相对于你的Post操作中的模型没有将整个路径放入隐藏的项目中。

例如,在html中你会看到

<input type="hidden" name="Id" />

但你真正想要的是

<input type="hidden" name="Products[0].Id" />

为了区分字段和项目。
当您尝试POST具有相同名称的两个不同隐藏输入时,仍然存在上述问题,但我还编写了一个测试项目,从客户端方面工作正常。
希望这可以帮助您:
控制器
public ActionResult Index()
        {
            Order order = new Order() { Id = 1 };
            order.Products = new List<Product>() { new Product() { Id = 3, OrderId = 1 } };
            return View("Order",order);
        }

模型

public class Order : IdentityBase
    {
        public List<Product> Products { get; set; }
    }

    public class Product : IdentityBase
    {
        public int OrderId { get; set; }
    }

 public class IdentityBase
    {
        public int Id { get; set; }
    }

视图

@model MvcApplication1.Models.Order

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Order</title>
</head>
<body>
    <div>
        @Html.HiddenFor(model => model.Id)
        @foreach (MvcApplication1.Models.Product product in Model.Products)
        {
            <div class="product">
                @Html.Partial("Product", product)
            </div>
        }
    </div>
</body>
</html>

局部视图

@model MvcApplication1.Models.Product

@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.OrderId)

客户端 HTML

<!DOCTYPE html> 
<html>
<head>    
    <title>Order</title>    
</head>    
<body>    
    <div>    
       <input id="Id" name="Id" type="hidden" value="1" />    
       <div class="product"> 
                <input id="Id" name="Id" type="hidden" value="3" />    
                <input id="OrderId" name="OrderId" type="hidden" value="1" />    
       </div>
    </div>
</body>


这是我们得出这一见解的来源: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx - Kaido
我知道如何为集合构建控件,但那不是我想做的。我只是想编辑第一个元素。从路径的部分可以看出,我希望在整个应用程序中重复使用产品编辑部分。 尝试使用@Html.Hidden("Id", Model.Id)产生了与HiddenFor相同(错误)的结果。然而,使用@Html.Hidden("P_Id", Model.Id)给出了预期的值,因此显然正确的值存在于局部视图中,但在某些时候被简单地覆盖了。 - Atrieos
你的Html表单范围是什么?你是否可能会提交两个名称为“Id”的元素?你可以在页面的html或POST操作的表单值集合中检查这一点。 - Kaido
1
我还没有进行POST操作。在收到GET请求的响应后,客户端上的值是错误的。如果我将“Id”字段的名称更改为其他内容,则可以向客户端输出正确的值,但如果不这样做,则似乎在组合视图的过程中某个时候会覆盖该值。可能是错误模型Order的Id被添加到部分字段中,或者请求URL中的Id以某种方式进入了我的字段中,我不确定。 - Atrieos
谢谢,那很有道理。HiddenFor命名约定不知道部分视图的深度,我们遇到过类似的问题。我记得我们尝试过类似的方法: HiddenFor(m=>m.Parent[m.Parent.IndexOf(m)].Id) 或者类似的方式,但是我需要创建一个测试项目来验证一下。 - Kaido
说实话,我宁愿它不知道。我的意图是在一些场合下重复使用这部分视图,即被编辑的产品既不是订单的子级,也不在父对象的列表中。产品对象应该能够自立,但如果我想让它起作用,我需要弄清楚如何防止其他对象的数据渗入我为产品设计的视图。 - Atrieos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接