自定义模型绑定,模型的状态和数据注解 [英] Custom model binding, model state, and data annotations

查看:139
本文介绍了自定义模型绑定,模型的状态和数据注解的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

1)是否有多​​余做验证自定义模型绑定,如果我有我的模型数据的注解,因为这是我想到的数据注解的点分别。

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

3)它是确定想自定义模型粘合剂作为构造方法,因为这是他们提醒我的。

首先,这里是我的模型。

 公共类项目
{
    [键]
    [需要]
    公众的Guid ProjectGuid {搞定;组; }    [需要]
    公共字符串帐户名{获得;组; }    [必需的(=的ErrorMessage项目名称要求)]
    [StringLength(128 =的ErrorMessage项目名称不能超过128个字符)]
    [MINLENGTH个(3 =的ErrorMessage项目名称必须至少为3个字符)]
    公共字符串名称{;组; }    [需要]
    众长TOTALTIME {搞定;组; }
}

然后我用一个自定义的模型绑定绑定模型的一些性质。请不要介意,它的快速和肮脏的只是试图让它工作,然后重构它。

 公共类ProjectModelBinder:IModelBinder
{
     公共对象BindModel(ControllerContext controllerContext,ModelBindingContext的BindingContext)
    {
        如果(controllerContext == NULL)
        {
            抛出新的ArgumentNullException(controllerContext);
        }
        如果(的BindingContext == NULL)
        {
            抛出新的ArgumentNullException(的BindingContext);
        }
        VAR P =新的项目();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get(名称);
        p.TotalTime = 0;        //
        //是因为数据注解这个多余的?!?!
        //
        如果(p.AccountName == NULL)
            bindingContext.ModelState.AddModelError(姓名,姓名为必填);        如果(p.AccountName.Length 3;)
            bindingContext.ModelState.AddModelError(名,最小长度为3个字符);        如果(p.AccountName.Length> 128)
            bindingContext.ModelState.AddModelError(姓名,最大长度为128个字符);        回磷;
    }
}

现在我的控制器动作。

  [HttpPost]
    公众的ActionResult的createProject([ModelBinder的(typeof运算(ProjectModelBinder))工程项目)
    {
        //
        //出于某种原因,模型的状态回来为有效,甚至当我强制错误
        //
        如果(!ModelState.IsValid)
            返回内容(Boolean.FalseString);        //_projectRepository.CreateProject(project);        返回内容(Boolean.TrueString);    }

修改

我发现了另一个问题计算器约code,但我不知道在这一点上我会注入下面的值到这个<一个href=\"http://stackoverflow.com/questions/2030059/dataannotation-validations-and-custom-modelbinder\">possible解决方案。

我要注入时创建一个新的对象是什么:

  VAR P =新的项目();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get(名称);
        p.TotalTime = 0;

我如何得到上面的code到什么是以下(可能的解决方案):

 公共类ProjectModelBinder:DefaultModelBinder
    {
        公众覆盖对象BindModel(ControllerContext controllerContext,ModelBindingContext的BindingContext)
        {
            如果(bindingContext.ModelType == typeof运算(项目))
            {
                ModelBindingContext newBindingContext =新ModelBindingContext()
                {                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        ()=&GT;新项目()//构造一个项目的对象,
                        typeof运算(项目)//使用项目元
                    )
                    的ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider                };                //调用默认的模型绑定这一新的绑定上下文
                返回base.BindModel(controllerContext,newBindingContext);
            }
            其他
            {
                返回base.BindModel(controllerContext,BindingContext中);
            }
        }    }}


解决方案

您会发现事情的工作容易得多,如果你继承了 DefaultModelBinder ,重写 BindModel 方法,调用 base.BindModel 方法,然后进行手动更改(设置GUID,帐户名称和总时间)。

1)它是多余的验证,你都做到了。你可以写code,以反映验证的元数据很像默认呢,还是只是因为你没有在你的模型绑定使用它删除数据注解的验证。

2)我不知道,这似乎是正确的,你应该逐步完成code,并确保您的自定义粘合剂填充所有适用的规则。

3)这是一个工厂肯定的,但没有这么多的构造函数。


编辑:你不能有任何接近的解决方案,只需设置你的模范工厂的功能需要的属性。

 公共类ProjectModelBinder:DefaultModelBinder
{
    公众覆盖对象BindModel(ControllerContext controllerContext,ModelBindingContext的BindingContext)
    {
        如果(bindingContext.ModelType == typeof运算(项目))
        {
            ModelBindingContext newBindingContext =新ModelBindingContext()
            {                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                    ()=&GT;新项目()//构造一个Project对象
                    {
                        ProjectGuid = System.Guid.NewGuid(),
                        帐户名= controllerContext.HttpContext.User.Identity.Name,
                        //不设置名称,这就是默认的粘结剂的工作
                        TOTALTIME = 0,
                    },
                    typeof运算(项目)//使用项目元
                )
                的ModelState = bindingContext.ModelState,
                ValueProvider = bindingContext.ValueProvider            };            //调用默认的模型绑定这一新的绑定上下文
            返回base.BindModel(controllerContext,newBindingContext);
        }
        其他
        {
            返回base.BindModel(controllerContext,BindingContext中);
        }
    }}

或者你也可以交替覆盖 CreateModel 方法:

 保护覆盖对象CreateModel(ControllerContext controllerContext,ModelBindingContext的BindingContext,System.Type的modelType)
{
    如果(modelType == typeof运算(项目))
    {
        项目模型=新项目()
        {
            ProjectGuid = System.Guid.NewGuid(),
            帐户名= controllerContext.HttpContext.User.Identity.Name,
            //不设置名称,这就是默认的粘结剂的工作
            TOTALTIME = 0,
        };        回归模型;
    }    抛出新NotSupportedException异常(你只能使用ProjectModelBinder上的项目类型的参数。);
}

I have a few questions regarding custom model binding, model state, and data annotations.

1) Is it redundant to do validation in the custom model binder if I have data annotations on my model, because that's what I thought the point of data annotations were.

2) Why is my controller treating the model state as valid even when it's not, mainly I make the Name property null or too short.

3) Is it ok to think of custom model binders as constructor methods, because that's what they remind me of.

First here is my model.

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

Then I'm using a custom model binder to bind some properties of the model. Please don't mind that it's quick and dirty just trying to get it functioning and then refactoring it.

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

Now my controller action.

    [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);

    }

EDIT

I Found some code on another stackoverflow question but I'm not sure at which point I would inject the following values into this possible solution.

What I want to inject when a new object is created:

        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;

How do I get the above code into what's below (Possible solution):

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

    }

}

解决方案

You will find things work much easier if you inherit from the DefaultModelBinder, override the BindModel method, call the base.BindModel method and then make the manual changes (setting the guid, account name and total time).

1) It is redundant to validate as you have done it. You could write code to reflect the validation metadata much like the default does, or just remove the data annotations validation since you are not using it in your model binder.

2) I don't know, it seems correct, you should step through the code and make sure your custom binder is populating all of the applicable rules.

3) It's a factory for sure, but not so much a constructor.


EDIT: you couldn't be any closer to the solution, just set the properties you need in the model factory function

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

}

Or you could alternately override the CreateModel method:

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

这篇关于自定义模型绑定,模型的状态和数据注解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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