在上篇文章:【原创】Asp.net MVC学习笔记之-基于类型来绑定Model的属性 中,例举了如何使用基于类型和基于用途2种方式结合来限制Model的绑定,由此引发了一个意想不到的问题。
先看一下前台页面的代码:
< p >
< label for = " Test1 " > Test1: </ label >
<%= Html.TextBox( " Test1 " ) %>
<%= Html.ValidationMessage( " Test1 " , " * " ) %>
</ p >
< p >
< label for = " Test2 " > Test2: </ label >
<%= Html.TextBox( " Test2 " ) %>
<%= Html.ValidationMessage( " Test2 " , " * " ) %>
</ p >
< p >
< label for = " Test3 " > Test3: </ label >
<%= Html.TextBox( " Test3 " ) %>
<%= Html.ValidationMessage( " Test3 " , " * " ) %>
</ p >
< p >
< label for = " Test4 " > Test4: </ label >
<%= Html.TextBox( " Test4 " ) %>
<%= Html.ValidationMessage( " Test4 " , " * " ) %>
</ p >
< p >
< label for = " Test5 " > Test5: </ label >
<%= Html.TextBox( " Test5 " ) %>
<%= Html.ValidationMessage( " Test5 " , " * " ) %>
</ p >
当页面输入的数据在Controller里经过数据绑定之后,只剩下了Test3一项有值,其他几个都是null,然后当程序走到
Html.TextBox的时候就出错了,报错如下:
堆栈跟踪:
[NullReferenceException: 未将对象引用设置到对象的实例。]
System.Web.Mvc.HtmlHelper.GetModelStateValue(String key, Type destinationType) +63
System.Web.Mvc.Html.InputExtensions.InputHelper(HtmlHelper htmlHelper, InputType inputType, String name, Object value, Boolean useViewData, Boolean isChecked, Boolean setId, Boolean isExplicitValue, IDictionary`2 htmlAttributes) +519
System.Web.Mvc.Html.InputExtensions.TextBox(HtmlHelper htmlHelper, String name, Object value, IDictionary`2 htmlAttributes) +34
System.Web.Mvc.Html.InputExtensions.TextBox(HtmlHelper htmlHelper, String name) +60
ASP.views_dinners_create_aspx.__RenderContent2(HtmlTextWriter __w, Control parameterContainer) in e:\学习资料\我的学习文件\工作学习\【12】-网页方向\MVC\一步一步学asp.net_mvc\Projects\NerdDinner\NerdDinner\Views\Dinners\Create.aspx:30
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +256
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
System.Web.UI.Control.Render(HtmlTextWriter writer) +10
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +99
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
ASP.views_shared_site_master.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in e:\学习资料\我的学习文件\工作学习\【12】-网页方向\MVC\一步一步学asp.net_mvc\Projects\NerdDinner\NerdDinner\Views\Shared\Site.Master:26
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +256
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
System.Web.UI.Control.Render(HtmlTextWriter writer) +10
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +99
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +134
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
System.Web.UI.Page.Render(HtmlTextWriter writer) +29
System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +59
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +99
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1266
找了半天也没找到原因,于是我把数据绑定的限制去掉试了一下,就没问题了,于是我想问题可能出在这个方法上面,祭出
Reflector,直接打开C盘下的System.Web.MVC.Dll,看个究竟:
通过HtmlHelper.TextBox方法,很容易跟到这个函数:
private static string InputHelper( this HtmlHelper htmlHelper, InputType inputType, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, IDictionary < string , object > htmlAttributes)
{
ModelState state;
if ( string .IsNullOrEmpty(name))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, " name " );
}
TagBuilder builder = new TagBuilder( " input " );
builder.MergeAttributes < string , object > (htmlAttributes);
builder.MergeAttribute( " type " , HtmlHelper.GetInputTypeString(inputType));
builder.MergeAttribute( " name " , name, true );
string str = Convert.ToString(value, CultureInfo.CurrentCulture);
bool flag = false ;
switch (inputType)
{
case InputType.CheckBox:
{
bool ? modelStateValue = htmlHelper.GetModelStateValue(name, typeof ( bool )) as bool ? ;
if (modelStateValue.HasValue)
{
isChecked = modelStateValue.Value;
flag = true ;
}
break ;
}
case InputType.Password:
if (value != null )
{
builder.MergeAttribute( " value " , str, isExplicitValue);
}
goto Label_0152;
case InputType.Radio:
break ;
default :
{
string str3 = ( string ) htmlHelper.GetModelStateValue(name, typeof ( string ));
builder.MergeAttribute( " value " , str3 ?? (useViewData ? htmlHelper.EvalString(name) : str), isExplicitValue);
goto Label_0152;
}
}
if ( ! flag)
{
string a = htmlHelper.GetModelStateValue(name, typeof ( string )) as string ;
if (a != null )
{
isChecked = string .Equals(a, str, StringComparison.Ordinal);
flag = true ;
}
}
if ( ! flag && useViewData)
{
isChecked = htmlHelper.EvalBoolean(name);
}
if (isChecked)
{
builder.MergeAttribute( " checked " , " checked " );
}
builder.MergeAttribute( " value " , str, isExplicitValue);
Label_0152:
if (setId)
{
builder.GenerateId(name);
}
if (htmlHelper.ViewData.ModelState.TryGetValue(name, out state) && (state.Errors.Count > 0 ))
{
builder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
if (inputType == InputType.CheckBox)
{
StringBuilder builder2 = new StringBuilder();
builder2.Append(builder.ToString(TagRenderMode.SelfClosing));
TagBuilder builder3 = new TagBuilder( " input " );
builder3.MergeAttribute( " type " , HtmlHelper.GetInputTypeString(InputType.Hidden));
builder3.MergeAttribute( " name " , name);
builder3.MergeAttribute( " value " , " false " );
builder2.Append(builder3.ToString(TagRenderMode.SelfClosing));
return builder2.ToString();
}
return builder.ToString(TagRenderMode.SelfClosing);
}
这里注意Switch应该是走到default里,继续跟,发现这个函数有问题:
internal object GetModelStateValue( string key, Type destinationType)
{
ModelState state;
if ( this .ViewData.ModelState.TryGetValue(key, out state))
{
return state.Value.ConvertTo(destinationType, null );
}
return null ;
}
其中这句话怎么看怎么不爽:return state.Value.ConvertTo(destinationType, null);
于是我在Controller里自己试了一把:
System.Web.Mvc.ModelState state;
if ( this .ViewData.ModelState.TryGetValue( " Test1 " , out state))
{
var result = state.Value.ConvertTo( typeof ( string ), null );
}
监视器打开,问题找到了
至于这后面隐藏了MVC的什么内部实现,等我搞清楚了再回头看看。今天的问题算是告一段落了。