如何DataAnnotationsModelBinder使用自定义的ViewModels工作? [英] How does DataAnnotationsModelBinder work with custom ViewModels?

查看:291
本文介绍了如何DataAnnotationsModelBinder使用自定义的ViewModels工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用 DataAnnotationsModelBinder在为了使用服务器端验证数据的注释在ASP.NET MVC。

I'm trying to use the DataAnnotationsModelBinder in order to use data annotations for server-side validation in ASP.NET MVC.

一切工作正常,只要我的视图模型只是一个简单的类,即时性,如

Everything works fine as long as my ViewModel is just a simple class with immediate properties such as

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

但是, DataAnnotationsModelBinder 导致试图用一个复杂的视图模型<当的NullReferenceException / code>,如

However, the DataAnnotationsModelBinder causes a NullReferenceException when trying to use a complex ViewModel, such as

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

    public Baz MyBazProperty {get;set;}
}

这是视图,使多个LINQ实体,因为我使用自定义的视图模型真是$ ​​P $ PFER包括几个LINQ的实体,而不是类型化的ViewData的一个大问题数组。

This is a big problem for views that render more than one LINQ entity because I really prefer using custom ViewModels that include several LINQ entities instead of untyped ViewData arrays.

DefaultModelBinder 不存在这个问题,所以看起来就好像在 DataAnnotationsModelBinder 的错误。有没有解决方法呢?

The DefaultModelBinder does not have this problem, so it seems like a bug in DataAnnotationsModelBinder. Is there any workaround to this?

编辑::一种可能的解决方法当然是要在这样的ViewModel类暴露子对象的属性:

A possible workaround is of course to expose the child object's properties in the ViewModel class like this:

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

不过我倒是preFER不要有写这些额外的code。在 DefaultModelBinder 正常工作与这种hiearchies,所以我想在 DataAnnotationsModelBinder 应该也是如此。

But I'd prefer not to have to write all this extra code. The DefaultModelBinder works fine with such hiearchies, so I suppose the DataAnnotationsModelBinder should as well.

第二个编辑:看起来这的确是在 DataAnnotationsModelBinder 的错误。然而,有希望,这可能在未来ASP.NET MVC框架版本之前的船舶固定。请参见这个论坛主题了解更多详情。

Second It looks like this is indeed a bug in DataAnnotationsModelBinder. However, there is hope this might be fixed before the next ASP.NET MVC framework version ships. See this forum thread for more details.

推荐答案

我今天所面临的确切同样的问题。就像我自己不直接配合我查看我的模型,但使用的持有模式和所有参数/配置我想发送到视图的一个实例中间ViewDataModel类。

I faced the exact same issue today. Like yourself I don't tie my View directly to my Model but use an intermediate ViewDataModel class that holds an instance of the Model and any parameters / configurations I'd like to sent of to the view.

我最终修改 BindProperty 的DataAnnotationsModelBinder规避的NullReferenceException ,我个人不喜欢的属性如果他们是唯一有效的约束(见下文原因)。

I ended up modifying BindProperty on the DataAnnotationsModelBinder to circumvent the NullReferenceException, and I personally didn't like properties only being bound if they were valid (see reasons below).

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, bindingContext, propertyDescriptor, newPropertyValue)) {

                OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, 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;
                    }
                }
            }
        }
    }
}

我也修改它,使它的总是绑定的属性的数据,无论它是否有效。这样我可以只通过模型回视图withouth的无效性被重置为null。

I also modified it so that it always binds the data on the property no matter if it's valid or not. This way I can just pass the model back to the view withouth invalid properties being reset to 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)
}

我的模型类(人)来自一个web服务,所以我不能把属性对他们直接,我解决了这个是如下的方式:

My Model class (Person) comes from a webservice so I can't put attributes on them directly, the way I solved this is as follows:

示例使用嵌套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正确结合他们。

Now if I bind a form field to [ViewDataModel.]Person.NearestGeographicRegion.Text & [ViewDataModel.]Person.NearestGeographicRegion.Value the ModelState starts validating them correctly and DataAnnotationsModelBinder binds them correctly as well.

这答案并不明确,这是今天下午抓我的头的产品。
它没有得到很好的测试,尽管它已经在通过单元测试该项目布赖恩·威尔逊开始,我的大部分有限的测试。关于这件事情真的结束我很想听听布拉德·威尔逊这个解决方案的想法。

This answer is not definitive, it's the product of scratching my head this afternoon. It's not been properly tested, eventhough it passed the unit tests in the project Brian Wilson started and most of my own limited testing. For true closure on this matter I would love to hear Brad Wilson thoughts on this solution.

这篇关于如何DataAnnotationsModelBinder使用自定义的ViewModels工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆