与模型对象更新的ModelState [英] Updating ModelState with model object

查看:144
本文介绍了与模型对象更新的ModelState的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:如何发帖+验证场景更新的ModelState

The problem: How to update ModelState in posting+validation scenario.

我有一个简单的形式:

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

当用户提交我想验证输入,并在某些情况下,我想解决用户的错误,让他知道,他所做的已经是固定的一个错误:

When user submits I want to validate input and in some circumstances I want to fix the error for user, letting him know that he made an error that is already fixed:

[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)。
出现这种情况,是因为文本框渲染checkes如果有一个ModelState中,如果它不为空 - 使用的ModelState的值。这个值当然是一个用户,然后再发布类型的。

Well the problem is, MVC will simply ignore the model passed to the view and will re-render whatever the user typed -- and not my value ("a"). This happens, because the TextBox renderer checkes if there is a ModelState and if it's not null - ModelState's value is used. That value is of course the one user typed before posting.

既然不能改变文本框渲染器的行为,我发现是由我自己来更新ModelState中唯一的解决方案。该quick'n'dirty的方法是(AB)使用DefaultModelBinder并覆盖从形式分配值,通过简单地改变分配方向建模的方法)。使用DefaultModelBinder我没有解析的ID。
下面code(基于原来实行DefaultModelBinder的)是我的解决方案是:

Since I can't change the behaviour of TextBox renderer the only solution I found would be to update the ModelState by myself. The quick'n'dirty way is to (ab)use the DefaultModelBinder and override the method that assigns the values from forms to model by simply changing the assignment direction ;). Using DefaultModelBinder I don't have to parse the ids. The following code (based on original implementation of DefaultModelBinder) is my solution to this:

/// <summary>
    /// Updates ModelState using values from <paramref name="order"/>
    /// </summary>
    /// <param name="order">Source</param>
    /// <param name="prefix">Prefix used by Binder. Argument name in Action (if not explicitly specified).</param>
    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<string>(prefix))
                {
                    var prefixKey = bindingContext.ModelState.Keys.First<string>(x => x == prefix);
                    bindingContext.ModelState[prefixKey].Value
                                    = new ValueProviderResult(obj2, obj2.ToString(),
                                                                bindingContext.ModelState[prefixKey].Value.Culture);
                }
            }
        }
    }

所以问题是:我该做的事情非常少见还是我失去了一些东西?如果是前者,那么我怎么能以更好的方式实现这样的功能(使用现有的基础设施MVC)?

So the question remains: am I doing something extremely uncommon or am I missing something? If the former, then how could I implement such functionality in a better way (using existing MVC infrastructure)?

推荐答案

我知道这个帖子是相当老,但它是一个问题,我以前也有,我只是想,我喜欢简单的解决方案 - 只是清除的ModelState后你已经得到了公布值。

I know this post is fairly old but it's a problem I've had before and I just thought of a simple solution that I like - just clear the ModelState after you've got the posted values.

UpdateModel(viewModel);
ModelState.Clear();

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

和视图必须使用(可能修改)视图模型对象,而不是ModelState中。

and the view has to use the (possibly modified) view model object rather than the ModelState.

这也许是真的很明显。如此看来在事后!

Maybe this is really obvious. It seems so in hindsight!

这篇关于与模型对象更新的ModelState的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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