同一页面上的ASP.NET MVC模型绑定相关实体

时间:2022-11-30 16:37:01

This problem has been driving me crazy for several hours now...

这个问题让我疯狂了好几个小时了......

In my domain, I have 2 entities that are related to each other Sku and Item. Each sku can have many items.

在我的域中,我有2个与Sku和Item相关的实体。每个sku可以有很多项目。

public class Sku
{
    private readonly EntitySet<Item> items;
    public Sku()
    {
        items = new EntitySet<Item>(AttachItems, DetachItems);
    }
    public int SkuId { get; set; }
    public string LongDescription { get; set; }
    public EntitySet<Item> Items
    {
        get { return items; }
        set{ items.Assign(value);}
    }
    private void AttachItems(Item entity)
    {
        entity.Sku = this;
    }
    private static void DetachItems(Item entity)
    {
        entity.Sku = null;
    }
}

public class Item
{
    public Sku Sku { get; set; }
    public int ItemId { get; set; }
    public string Category { get; set; }
    public string Description { get; set; }
}

I am building a page that will allow the end-user to update some fields on the sku and some fields on each item at the same time.

我正在构建一个页面,允许最终用户同时更新sku上的某些字段和每个项目的某些字段。

<% using (Html.BeginForm("Save", "Merchant", FormMethod.Post, 
       new { enctype = "multipart/form-data" })) { %>       
<fieldset>
    <legend>Sku</legend>
    <p><label for="SkuId">SkuId:</label>
       <%= Html.TextBox("SkuId", Model.SkuId, 
           new{@readonly="readonly",onfocus="this.blur();"}) %></p>
    <p><label for="LongDescription">LongDescription:</label>
       <%= Html.TextBox("LongDescription", Model.LongDescription) %></p>
</fieldset>
<% for (int i = 0; i < Model.Items.Count; i++) { %>       
<fieldset>
    <legend>Item</legend>
    <p><label for="ItemId">ItemId:</label>
       <%= Html.TextBox(string.Format("items[{0}].{1}", i, "ItemId"), 
           Model.Items[i].ItemId, 
           new { @readonly = "readonly", onfocus = "this.blur();" })%></p>
    <p><label for="Category">Category:</label>
       <%= Html.TextBox(string.Format("items[{0}].{1}", i, "Category"), 
           Model.Items[i].Category)%></p>
    <p><label for="Description">Description:</label>
       <%= Html.TextBox(string.Format("items[{0}].{1}", i, "Description"), 
           Model.Items[i].Description)%></p>
</fieldset>
<%} // for-loop %>
    <p><input type="submit" value="Save" /></p>
<%} // form %> 

I have some controller code that works by accepting both a Sku and an EntitySet of Item and then assigning the Items to the Sku.

我有一些控制器代码,通过接受项目的Sku和EntitySet,然后将项目分配给Sku。

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Save(Sku sku, EntitySet<Item> items)
    {
        if (sku != null)
        {
            if (items != null)
            {
                sku.Items.Assign(items);
            }
        }

        // save Sku to repository ...
        // return Details view ...
    }

This works, however I have noticed that it makes two trips through the DefaultModelBinder for each Item in addition to one trip for the Sku. When the Sku is bound, the setter for Items is called, and the binder even passes in a hydrated Items collection with the correct values. However, after the call to items.Assign, Items.Count is 0. This is why I have to re-assign the items in the controller code. I was expecting the items to be transferred over to the Items collection by the binder. This should eliminate the extra trip per item, since the items parameter on my controller method could be removed. Why isn’t this working?

这是有效的,但是我注意到除了Sku的一次旅行之外,它还为每个项目通过DefaultModelBinder进行了两次旅行。绑定Sku时,将调用Items的setter,并且binder甚至会使用正确的值传递水合Items集合。但是,在调用items.Assign之后,Items.Count为0.这就是我必须重新分配控制器代码中的项目的原因。我期待通过活页夹将项目转移到Items集合。这应该消除每个项目的额外行程,因为我的控制器方法上的items参数可以被删除。为什么这不起作用?

3 个解决方案

#1


1  

You might need to create a custom model binder for this?

您可能需要为此创建自定义模型绑定器?

#2


1  

Hopefully, I am understanding you problem correctly...

希望我能正确理解你的问题......

Rather than defining your Save action with 2 parameters, have you tried just defining it with a single parameter, of type Sku?

您是否尝试使用Sku类型的单个参数定义它,而不是使用2个参数定义“保存”操作?

You would then want to redefine the item HTML controls similar to the following example...

然后,您需要重新定义项目HTML控件,类似于以下示例...

<%=
    Html.TextBox
    (
        string.Format("sku.Items[{0}].{1}", i, "ItemId"), 
        Model.Items[i].ItemId, 
        new { @readonly = "readonly", onfocus = "this.blur();" }
    )
%>


This way, you're populating the items directly in the Sku object itself.

这样,您就可以直接在Sku对象本身中填充项目。


Another potential solution would be to add an additional field to your Item class, such as...

另一个可能的解决方案是在Item类中添加一个额外的字段,例如......

Int32 SkuId { get; set; }

This way, you could define an additional hidden field for each item in your view that would be auto-bound to the SkuId of each item back at the controller.

这样,您可以为视图中的每个项目定义一个额外的隐藏字段,该字段将自动绑定到控制器上每个项目的SkuId。

<%=
    Html.Hidden
    (
        string.Format("sku.Items[{0}].{1}", i, "SkuId"), 
        Model.Items[i].SkuId
    )
%>

You could then just update your items collection independently of your Sku object. Regardless of which way you go, the two collections have to explicitly tell Linq to SQL to update the sku and items anyhow.

然后,您可以独立于Sku对象更新项目集合。无论你走哪条路,两个集合都必须明确地告诉Linq SQL以无论如何更新sku和项目。

You could also define your own binder class, but that's probably more work than it's worth in this case. Just follow the ASP.NET MVC conventions, and I think you should be able to find something that will work without feeling like it's a hack.

您也可以定义自己的活页夹类,但在这种情况下,这可能比它的价值更多。只需遵循ASP.NET MVC约定,我认为你应该能够找到一些可以工作的东西而不会觉得它是一个黑客。

#3


0  

I had a similar issue where EntitySets weren't bound properly in my ViewModel.

我有一个类似的问题,其中EntitySets在我的ViewModel中没有正确绑定。

What I did was to create another property called Mvc[YourEntitySetPropertyName], as a generic list, that wrapped around the private fields of the EntitySet:

我所做的是创建另一个名为Mvc [YourEntitySetPropertyName]的属性,作为通用列表,包含EntitySet的私有字段:

public List<InvoiceLineItem> MvcInvoiceLineItemList
    {
        get { return _invoiceLineItemList.ToList(); }
        set { _invoiceLineItemList.AddRange(value); }
    }

I then used that instead of the EntitySet property on my view markup.

然后我在视图标记上使用它而不是EntitySet属性。

There will be no need to pass the items in your controller method signature- just pass the Sku at that point:

没有必要传递控制器方法签名中的项目 - 只需在此时通过Sku:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku)
{
    if (sku != null)
    {
       // save Sku to repository ...
       // return Details view ...
    }

}

#1


1  

You might need to create a custom model binder for this?

您可能需要为此创建自定义模型绑定器?

#2


1  

Hopefully, I am understanding you problem correctly...

希望我能正确理解你的问题......

Rather than defining your Save action with 2 parameters, have you tried just defining it with a single parameter, of type Sku?

您是否尝试使用Sku类型的单个参数定义它,而不是使用2个参数定义“保存”操作?

You would then want to redefine the item HTML controls similar to the following example...

然后,您需要重新定义项目HTML控件,类似于以下示例...

<%=
    Html.TextBox
    (
        string.Format("sku.Items[{0}].{1}", i, "ItemId"), 
        Model.Items[i].ItemId, 
        new { @readonly = "readonly", onfocus = "this.blur();" }
    )
%>


This way, you're populating the items directly in the Sku object itself.

这样,您就可以直接在Sku对象本身中填充项目。


Another potential solution would be to add an additional field to your Item class, such as...

另一个可能的解决方案是在Item类中添加一个额外的字段,例如......

Int32 SkuId { get; set; }

This way, you could define an additional hidden field for each item in your view that would be auto-bound to the SkuId of each item back at the controller.

这样,您可以为视图中的每个项目定义一个额外的隐藏字段,该字段将自动绑定到控制器上每个项目的SkuId。

<%=
    Html.Hidden
    (
        string.Format("sku.Items[{0}].{1}", i, "SkuId"), 
        Model.Items[i].SkuId
    )
%>

You could then just update your items collection independently of your Sku object. Regardless of which way you go, the two collections have to explicitly tell Linq to SQL to update the sku and items anyhow.

然后,您可以独立于Sku对象更新项目集合。无论你走哪条路,两个集合都必须明确地告诉Linq SQL以无论如何更新sku和项目。

You could also define your own binder class, but that's probably more work than it's worth in this case. Just follow the ASP.NET MVC conventions, and I think you should be able to find something that will work without feeling like it's a hack.

您也可以定义自己的活页夹类,但在这种情况下,这可能比它的价值更多。只需遵循ASP.NET MVC约定,我认为你应该能够找到一些可以工作的东西而不会觉得它是一个黑客。

#3


0  

I had a similar issue where EntitySets weren't bound properly in my ViewModel.

我有一个类似的问题,其中EntitySets在我的ViewModel中没有正确绑定。

What I did was to create another property called Mvc[YourEntitySetPropertyName], as a generic list, that wrapped around the private fields of the EntitySet:

我所做的是创建另一个名为Mvc [YourEntitySetPropertyName]的属性,作为通用列表,包含EntitySet的私有字段:

public List<InvoiceLineItem> MvcInvoiceLineItemList
    {
        get { return _invoiceLineItemList.ToList(); }
        set { _invoiceLineItemList.AddRange(value); }
    }

I then used that instead of the EntitySet property on my view markup.

然后我在视图标记上使用它而不是EntitySet属性。

There will be no need to pass the items in your controller method signature- just pass the Sku at that point:

没有必要传递控制器方法签名中的项目 - 只需在此时通过Sku:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku)
{
    if (sku != null)
    {
       // save Sku to repository ...
       // return Details view ...
    }

}