当前位置:  开发笔记 > 后端 > 正文

使用模型对象更新ModelState

如何解决《使用模型对象更新ModelState》经验,为你挑选了1个好方法。

问题:如何在发布+验证方案中更新ModelState.

我有一个简单的形式:

<%= Html.ValidationSummary() %>
<% using(Html.BeginForm())%>
<%{ %>
    <%=Html.TextBox("m.Value") %>
    
<%} %>

当用户提交我想要验证输入时,在某些情况下我想为用户修复错误,让他知道他犯了一个已修复的错误:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(M m)
{
    if (m.Value != "a")
    {
        ModelState.AddModelError("m.Value", "should be \"a\"");
        m.Value = "a";
        return View(m);
    }
    return View("About");            
}

问题是,MVC将简单地忽略传递给视图的模型,并将重新呈现用户键入的内容 - 而不是我的值("a").发生这种情况,因为TextBox渲染器检查是否存在ModelState,如果它不为null,则使用ModelState的值.该值当然是在发布之前键入的一个用户.

由于我无法更改TextBox渲染器的行为,因此我找到的唯一解决方案是自己更新ModelState.quick'n'dirty方法是(ab)使用DefaultModelBinder并覆盖通过简单地更改赋值方向将表单中的值分配给模型的方法;).使用DefaultModelBinder我不必解析id.以下代码(基于DefaultModelBinder的原始实现)是我的解决方案:

/// 
    /// Updates ModelState using values from 
    /// 
    /// Source
    /// Prefix used by Binder. Argument name in Action (if not explicitly specified).
    protected void UpdateModelState(object model, string prefix)
    {
        new ReversedBinder().BindModel(this.ControllerContext,
            new ModelBindingContext()
            {
                Model = model,
                ModelName = prefix,
                ModelState = ModelState,
                ModelType = model.GetType(),
                ValueProvider = ValueProvider
            });
    }

    private class ReversedBinder : DefaultModelBinder
    {
        protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
        {
            string prefix = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
            object val = typeof(Controller)
                .Assembly.GetType("System.Web.Mvc.DictionaryHelpers")
                .GetMethod("DoesAnyKeyHavePrefix")
                .MakeGenericMethod(typeof(ValueProviderResult))
                .Invoke(null, new object[] { bindingContext.ValueProvider, prefix });
            bool res = (bool)val;
            if (res)
            {

                IModelBinder binder = new ReversedBinder();//this.Binders.GetBinder(propertyDescriptor.PropertyType);
                object obj2 = propertyDescriptor.GetValue(bindingContext.Model);

                ModelBindingContext context2 = new ModelBindingContext();
                context2.Model = obj2;
                context2.ModelName = prefix;
                context2.ModelState = bindingContext.ModelState;
                context2.ModelType = propertyDescriptor.PropertyType;
                context2.ValueProvider = bindingContext.ValueProvider;
                ModelBindingContext context = context2;
                object obj3 = binder.BindModel(controllerContext, context);

                if (bindingContext.ModelState.Keys.Contains(prefix))
                {
                    var prefixKey = bindingContext.ModelState.Keys.First(x => x == prefix);
                    bindingContext.ModelState[prefixKey].Value
                                    = new ValueProviderResult(obj2, obj2.ToString(),
                                                                bindingContext.ModelState[prefixKey].Value.Culture);
                }
            }
        }
    }

所以问题仍然存在:我做的事情是非常不寻常的还是我错过了什么?如果是前者,那么我怎样才能以更好的方式实现这些功能(使用现有的MVC基础设施)?



1> 小智..:

我知道这篇文章相当陈旧,但这是我之前遇到的一个问题,我只是想到了一个我喜欢的简单解决方案 - 只需在获得发布值后清除ModelState.

UpdateModel(viewModel);
ModelState.Clear();

viewModel.SomeProperty = "a new value";
return View(viewModel);

并且视图必须使用(可能已修改的)视图模型对象而不是ModelState.

也许这是非常明显的.事后看来似乎如此!

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有