部分更新ASP.NET MVC模型的最佳方式('merge'用户提交的表单与模型)

时间:2013-10-09 19:12:00

标签: asp.net-mvc asp.net-mvc-4 model asp.net-mvc-5 model-binding

我的问题与this stack overflow question中提出的情况基本相同,我发现自己想要从数据库加载现有的有效版本的模型,并将其中的一部分更新为某个字段的子集暴露在我的网络表格上。

无论如何,我可以让模型绑定过程保证我的ID属性首先被绑定吗?

如果我可以保证这一点,那么,在我的ViewModel的ID属性的setter中,我可以触发'load',以便最初从DB(或WCF服务..或Xml文件)填充该对象。或者其他选择的存储库)然后,随着MVC完成模型绑定过程,从FORM帖子提交的其余属性整齐地合并到对象中。

然后,我的IValidatableObject.Validate方法逻辑将很好地告诉我结果对象是否仍然有效......依此类推......

在我看来,我必须编写管道,其中我有两个模型实例(knownValidDomainModelInstanceFromStorage,postedPartialViewModelInstanceFromForm)然后手动映射到所需的属性是重复一些真正已经由MVC处理的东西......如果我能只控制身份的绑定顺序。


编辑 - 我发现属性绑定顺序可以使用自定义绑定器进行操作。非常简单地。阅读我在下面发布的答案。仍然欢迎您的反馈或意见。

1 个答案:

答案 0 :(得分:0)

好的,在阅读了如何自定义默认模型绑定器之后,我认为这段代码可能会对排序属性进行排序,并且每次都会给我所需的绑定顺序。基本上允许我首先绑定标识属性(从而允许我让我的视图模型触发'加载'),允许模型绑定过程的其余部分基本上作为合并运行!

''' <summary>
''' A derivative of the DefaultModelBinder that ensures that desired properties are put first in the binding order.
''' </summary>
''' <remarks>
''' When view models can reliably expect a bind of their key identity properties first, they can then be designed trigger a load action 
''' from their repository. This allows the remainder of the binding process to function as property merge.
''' </remarks>
Public Class BindIdFirstModelBinder
        Inherits DefaultModelBinder

    Private commonIdPropertyNames As String() = {"Id"}
    Private sortedPropertyCollection As ComponentModel.PropertyDescriptorCollection

    Public Sub New()
        MyBase.New()
    End Sub

    ''' <summary>
    ''' Use this constructor to declare specific properties to look for and move to top of binding order.
    ''' </summary>
    ''' <param name="propertyNames"></param>
    ''' <remarks></remarks>
    Public Sub New(propertyNames As String())
        MyBase.New()
        commonIdPropertyNames = propertyNames
    End Sub

    Protected Overrides Function GetModelProperties(controllerContext As ControllerContext, bindingContext As ModelBindingContext) As ComponentModel.PropertyDescriptorCollection
        Dim rawCollection = MyBase.GetModelProperties(controllerContext, bindingContext)

        Me.sortedPropertyCollection = rawCollection.Sort(commonIdPropertyNames)

        Return sortedPropertyCollection
    End Function

End Class

然后,我可以注册这个代替我的DefaultModelBinder,并提供我想要“浮动”到ModelBinding进程顶部的最常见的属性名称......

    Sub Application_Start()

            RouteConfig.RegisterRoutes(RouteTable.Routes)
            BundleConfig.RegisterBundles(BundleTable.Bundles)
            ' etc... other standard config stuff omitted...
            ' override default model binder:
            ModelBinders.Binders.DefaultBinder = New BindIdFirstModelBinder({"Id", "WorkOrderId", "CustomerId"})
    End Sub