Asp.net mvc 自定义模型绑定、模型状态和数据注释

Asp.net mvc 自定义模型绑定、模型状态和数据注释,asp.net-mvc,validation,data-annotations,modelbinders,modelstate,Asp.net Mvc,Validation,Data Annotations,Modelbinders,Modelstate,关于自定义模型绑定、模型状态和数据注释,我有一些问题 1) 如果我的模型上有数据注释,那么在自定义模型绑定器中进行验证是否是多余的,因为我认为数据注释的意义就在于此 2) 为什么我的控制器在模型状态无效的情况下仍将其视为有效状态,主要是我将Name属性设置为null或太短 3) 可以将自定义模型绑定器看作构造函数方法吗,因为这正是它们提醒我的 首先,这是我的模型 public class Projects { [Key] [Required] public Gu

关于自定义模型绑定、模型状态和数据注释,我有一些问题

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;
    }
}
编辑

我在另一个stackoverflow问题上找到了一些代码,但我不确定在哪一点我会将以下值注入到这个问题中

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

        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;
如何将上述代码转换为以下内容(可能的解决方案):


如果从
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.");
}

嘿,你介意检查我的帖子吗?我在底部对它进行了编辑,并提出了一个可能的解决方案,我只是不知道下一步该怎么做。Modelbinding对我来说还是个新鲜事物。或许你有一个更简单的方法来实现同样的事情。@Odnxe:很难比你更接近,我在MVC3中实现了这个,以再次检查它是否按预期工作(或者至少我理解你的意图)。太棒了,谢谢你最后的一点帮助!我知道我离模型绑定和MVC已经很近了,但我对模型绑定和MVC还不是很熟悉。你帮我省去了好几个小时的头痛。两种解决方案都有效,这只是一种偏好还是另一种?还是一种方法天生就比另一种好?
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);
        }
    }

}
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.");
}