asp.net-mvc – DataAnnotationsModelBinder如何使用自定义ViewModel?

我正在尝试使用 DataAnnotationsModelBinder,以便在ASP.NET MVC中使用数据注释进行服务器端验证.

只要我的viewmodel只是一个具有直接属性的简单类,一切都可以正常工作

public class Foo
{
    public int Bar {get;set;}
}

但是,DataAnnotationsModelBinder在尝试使用复杂的viewmodel时会导致NullReferenceException,例如

public class Foo
{
    public class Baz
    {
        public int Bar {get;set;}
    }

    public Baz MyBazProperty {get;set;}
}

对于渲染多个LINQ实体的视图来说,这是一个很大的问题,因为我更喜欢使用包含多个LINQ实体而不是无类型ViewData数组的自定义viewmodel.

DefaultModelBinder没有这个问题,所以它似乎是DataAnnotationsModelBinder中的一个错误.这有什么解决方法吗?

编辑:一种可能的解决方法当然是在viewmodel类中公开子对象的属性,如下所示:

public class Foo
{
    private Baz myBazInstance;

    [required]
    public string ExposedBar
    {
        get { return MyBaz.Bar; }
        set { MyBaz.Bar = value; }
    }

    public Baz MyBaz
    {
        get { return myBazInstance ?? (myBazInstance = new Baz()); }
        set { myBazInstance = value; }
    }

    #region nested type: Baz

    public class Baz
    {
        [required]
        public string Bar { get; set; }
    }

    #endregion
}

#endregion

但我宁愿不必编写所有这些额外的代码. DefaultModelBinder可以正常使用这样的hiearchies,所以我认为DataAnnotationsModelBinder也应该如此.

第二次编辑:看起来这确实是DataAnnotationsModelBinder中的一个错误.但是,希望在下一个ASP.NET MVC框架版本发布之前可以修复它.有关详细信息,请参见this forum thread.

解决方法

我今天遇到了同样的问题.像我自己一样,我没有将我的View直接绑定到我的模型,而是使用一个间的ViewDataModel类,它包含Model的一个实例以及我想发送给视图的任何参数/配置.

我最终修改了DataAnnotationsModelBinder上的BindProperty来绕过NullReferenceException,我个人不喜欢只有绑定它们才有效(见下面的原因).

protected override void BindProperty(ControllerContext controllerContext,ModelBindingContext bindingContext,PropertyDescriptor propertyDescriptor) {
    string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName,propertyDescriptor.Name);

    // Only bind properties that are part of the request
    if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) {
        var innerContext = new ModelBindingContext() {
            Model = propertyDescriptor.GetValue(bindingContext.Model),ModelName = fullPropertyKey,ModelState = bindingContext.ModelState,ModelType = propertyDescriptor.PropertyType,ValueProvider = bindingContext.ValueProvider
        };

        IModelBinder binder = Binders.GetBinder(propertyDescriptor.PropertyType);
        object newPropertyValue = ConvertValue(propertyDescriptor,binder.BindModel(controllerContext,innerContext));
        ModelState modelState = bindingContext.ModelState[fullPropertyKey];
        if (modelState == null)
        {
            var keys = bindingContext.ValueProvider.FindKeysWithPrefix(fullPropertyKey);
            if (keys != null && keys.Count() > 0)
                modelState = bindingContext.ModelState[keys.First().Key];
        }
        // Only validate and bind if the property itself has no errors
        //if (modelState.Errors.Count == 0) {
            SetProperty(controllerContext,bindingContext,propertyDescriptor,newPropertyValue);
            if (OnPropertyValidating(controllerContext,newPropertyValue)) {

                OnPropertyValidated(controllerContext,newPropertyValue);
            }
        //}

        // There was an error getting the value from the binder,which was probably a format
        // exception (meaning,the data wasn't appropriate for the field)
        if (modelState.Errors.Count != 0) {
            foreach (var error in modelState.Errors.Where(err => err.ErrorMessage == "" && err.Exception != null).ToList()) {
                for (var exception = error.Exception; exception != null; exception = exception.InnerException) {
                    if (exception is FormatException) {
                        string displayName = GetdisplayName(propertyDescriptor);
                        string errorMessage = InvalidValueFormatter(propertyDescriptor,modelState.Value.AttemptedValue,displayName);
                        modelState.Errors.Remove(error);
                        modelState.Errors.Add(errorMessage);
                        break;
                    }
                }
            }
        }
    }
}

我也对它进行了修改,以便它始终绑定属性上的数据,无论它是否有效.这样我就可以将模型传递回视图,而无需将无效属性重置为null.

控制器摘录

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(ProfileViewDataModel model)
{
    FormCollection form = new FormCollection(this.Request.Form);
    wsPerson service = new wsPerson();
    Person newPerson = service.Select(1,-1);
    if (ModelState.IsValid && TryUpdateModel<IPersonBindable>(newPerson,"Person",form.TovalueProvider()))
    {
        //call wsPerson.save(newPerson);
    }
    return View(model); //model.Person is always bound no null properties (unless they were null to begin with)
}

我的Model类(Person)来自web服务,所以我不能直接将属性放在它们上面,我解决这个问题的方法如下:

嵌套DataAnnotations的示例

[Validation.MetadataType(typeof(PersonValidation))]
public partial class Person : IPersonBindable { } //force partial.

public class PersonValidation
{
    [Validation.Immutable]
    public int Id { get; set; }
    [Validation.required]
    public string FirstName { get; set; }
    [Validation.StringLength(35)]
    [Validation.required]
    public string LastName { get; set; }
    CategoryItemNullable NearestGeographicRegion { get; set; }
}

[Validation.MetadataType(typeof(CategoryItemNullableValidation))]
public partial class CategoryItemNullable { }

public class CategoryItemNullableValidation
{
    [Validation.required]
    public string Text { get; set; }
    [Validation.Range(1,10)]
    public string Value { get; set; }
}

现在,如果我将表单字段绑定到[ViewDataModel.] Person.NearestGeographicRegion.Text& [ViewDataModel.] Person.NearestGeographicRegion.Value ModelState开始正确验证它们,DataAnnotationsModelBinder也正确绑定它们.

这个答案不是决定性的,它是今天下午挠头的产物.
它没有经过适当的测试,尽管它已经通过了the project布莱恩威尔逊开始的单元测试,并且我自己进行了大部分有限的测试.为了真正关闭这个问题,我很想听听Brad Wilson关于这个解决方案的想法.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要讲解了“WPF如何实现带筛选功能的DataGrid”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“WPF...
本篇内容介绍了“基于WPF如何实现3D画廊动画效果”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这...
Some samples are below for ASP.Net web form controls:(from http://www.visualize.uk.com/resources/asp
问题描述: 对于未定义为 System.String 的列,唯一有效的值是(引发异常)。 For columns not defined as System.String, the only vali
最近用到了CalendarExtender,结果不知道为什么发生了错位,如图在Google和百度上找了很久,中文的文章里面似乎只提到了如何本地化(就是显示中文的月份)以及怎么解决被下拉框挡住的问题,谈
ASP.NET 2.0 page lifecyle ASP.NET 2.0 event sequence changed a lot since 1.1. Here is the order: App
静态声明: &#39; Style=&quot;position: relative&quot; AppendDataBoundItems=&quot;True&quot;&gt; (无 或 空 或
以下内容是从网络上搜集资料,然后整理而来的。不当之处,请不吝指教。(The following were from network, and edited by myself. Thanks in a
Imports System Imports System.Reflection Namespace DotNetNuke &#39;*********************************
Ok so you have all seen them: “8 million tools for web development”, “5 gagillion tools that if you
以下内容来源于: http://blog.csdn.net/cuike519/archive/2005/09/27/490316.aspx 问:为什么Session在有些机器上偶尔会丢失? 答:可能和
以下文章提到可以用“http://localhost/MyWebApp/WebAdmin.axd”管理站点: ---------------------------------------------
Visual Studio 2005 IDE相关的11个提高开发效率的技巧 英文原创来源于: http://www.chinhdo.com/chinh/blog/20070920/top-11-vis
C#日期格式化 from: http://51xingfu.blog.51cto.com/219185/46222 日期转化一 为了达到不同的显示效果有时,我们需要对时间进行转化,默认格式为:2007
from: http://www.nikhilk.net/UpdateControls.aspx Two controls that go along with the UpdatePanel and
Open the report in the Designer. In the ToolBox, select/expand the &quot;Report Items&quot; section.
from: http://drupal.org/node/75844 Do this: find which TinyMCE theme you are using. For the sake of
asp.net中给用户控件添加自定义事件 用户控件中定义好代理和事件: public delegate void ItemSavedDelegate(object sender, EventArgs
在Windows版本的Safari中浏览以下的页面。 http://www.asp.net/AJAX/Control-Toolkit/Live/Calendar/Calendar.aspx Calen
http://aspnet.4guysfromrolla.com/articles/021506-1.aspx By Scott Mitchell Introduction When creating