【问题标题】:Custom model binding, model state, and data annotations自定义模型绑定、模型状态和数据注释
【发布时间】:2011-08-14 19:20:57
【问题描述】:

我有几个关于自定义模型绑定、模型状态和数据注释的问题。

1) 如果我的模型上有数据注释,那么在自定义模型绑定器中进行验证是否是多余的,因为这就是我认为数据注释的意义所在。

2) 为什么我的控制器将模型状态视为有效,即使它不是,主要是我将 Name 属性设置为 null 或太短。

3) 是否可以将自定义模型绑定器视为构造函数方法,因为它们让我想起了这一点。

首先是我的模型。

public class Projects
{     
    [Key]
    [Required]
    public Guid ProjectGuid { get; set; }

    [Required]
    public string AccountName { get; set; }

    [Required(ErrorMessage = "Project name required")]
    [StringLength(128, ErrorMessage = "Project name cannot exceed 128 characters")]
    [MinLength(3, ErrorMessage = "Project name must be at least 3 characters")]
    public string Name { get; set; }

    [Required]
    public long TotalTime { get; set; }
}

然后我使用自定义模型绑定器来绑定模型的一些属性。请不要介意它只是试图让它运行然后重构它是快速和肮脏的。

public class ProjectModelBinder : IModelBinder
{
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }
        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

        //
        // Is this redundant because of the data annotations?!?!
        //
        if (p.AccountName == null)
            bindingContext.ModelState.AddModelError("Name", "Name is required");

        if (p.AccountName.Length < 3)
            bindingContext.ModelState.AddModelError("Name", "Minimum length is 3 characters");

        if (p.AccountName.Length > 128)
            bindingContext.ModelState.AddModelError("Name", "Maximum length is 128 characters");

        return p;
    }
}

现在我的控制器动作。

    [HttpPost]
    public ActionResult CreateProject([ModelBinder(typeof(ProjectModelBinder))]Project project)
    {
        //
        // For some reason the model state comes back as valid even when I force an error
        //
        if (!ModelState.IsValid)
            return Content(Boolean.FalseString);

        //_projectRepository.CreateProject(project);

        return Content(Boolean.TrueString);

    }

编辑

我在另一个 stackoverflow 问题上找到了一些代码,但我不确定何时将以下值注入此 possible solution.

创建新对象时我想注入的内容:

        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

如何将上面的代码放入下面的代码(可能的解决方案):

    public class ProjectModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(Project))
            {
                ModelBindingContext newBindingContext = new ModelBindingContext()
                {

                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new Project(), // construct a Project object,
                        typeof(Project)         // using the Project metadata
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider

                };

                // call the default model binder this new binding context
                return base.BindModel(controllerContext, newBindingContext);
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }

    }

}

【问题讨论】:

    标签: asp.net-mvc validation data-annotations modelbinders modelstate


    【解决方案1】:

    如果您从DefaultModelBinder 继承,覆盖BindModel 方法,调用base.BindModel 方法,然后进行手动更改(设置guid、帐户名称和总时间),您会发现事情会更容易。

    1) 按照您的操作进行验证是多余的。您可以编写代码来反映验证元数据,就像默认设置一样,或者只是删除数据注释验证,因为您没有在模型绑定器中使用它。

    2) 我不知道,这似乎是正确的,您应该单步执行代码并确保您的自定义活页夹填充了所有适用的规则。

    3) 肯定是工厂,但不是构造函数。


    编辑:你不可能更接近解决方案,只需在模型工厂函数中设置你需要的属性

    public class ProjectModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(Project))
            {
                ModelBindingContext newBindingContext = new ModelBindingContext()
                {
    
                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new Project()  // construct a Project object
                        {
                            ProjectGuid = System.Guid.NewGuid(),
                            AccountName = controllerContext.HttpContext.User.Identity.Name,
                            // don't set name, thats the default binder's job
                            TotalTime = 0,
                        }, 
                        typeof(Project)         // using the Project metadata
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider
    
                };
    
                // call the default model binder this new binding context
                return base.BindModel(controllerContext, newBindingContext);
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }
    
    }
    

    或者您也可以覆盖CreateModel 方法:

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
    {
        if (modelType == typeof(Project))
        {
            Project model = new Project()
            {
                ProjectGuid = System.Guid.NewGuid(),
                AccountName = controllerContext.HttpContext.User.Identity.Name,
                // don't set name, thats the default binder's job
                TotalTime = 0,
            };
    
            return model;
        }
    
        throw new NotSupportedException("You can only use the ProjectModelBinder on parameters of type Project.");
    }
    

    【讨论】:

    • 嘿,你介意检查一下我的帖子吗?我在底部用一个可能的解决方案对其进行了编辑,我只是​​不知道下一步该怎么做。模型绑定对我来说仍然很新鲜。
    • 或者你有更简单的方法来实现同样的目标。
    • @Odnxe:很难比你更接近,我在 MVC3 中实现了这个以仔细检查它是否按预期工作(或者至少我对你的意图的理解)。
    • 太棒了,谢谢你最后一点帮助!我知道我很接近我对模型绑定和 MVC 很陌生。您为我节省了数小时的头痛时间。
    • 两种解决方案都有效,是否只是一种偏好?还是一种方法天生就比另一种更好?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2020-02-14
    • 1970-01-01
    • 2012-02-18
    • 2012-01-29
    相关资源
    最近更新 更多