问题
先看下面一个简单的 ASP.NET MVC 5
的 demo:
- model
public class TestModel
{
public List<int> Ints { get; set; }
}
- controller
public ActionResult Index()
{
var testModel = new TestModel();
return View(testModel);
}
[ActionName("Index"), HttpPost]
public ActionResult Post(TestModel testModel)
{
return View(testModel);
}
- view
@model Test.Controllers.TestModel
<form action="@Url.Action("Index")" method="post">
@for (var i = 0; i < 10; i++)
{
@Html.TextBoxFor(model => model.Ints[i])
}
<input type="submit" value="Submit" />
</form>
有没有看出什么问题?
View
里面的
@for (var i = 0; i < 10; i++)
{
Html.TextBoxFor(model => model.Ints[i])
}
在 Model.Ints
并没有初始化的情况下被使用了。
正常情况下可能会这么写:
@{
if (Model.Ints == null)
{
Model.Ints = new List<int>();
}
for (var i = 0; i < Model.Ints.Count; i++)
{
@Html.TextBoxFor(model => model.Ints[i])
}
}
如果我们需要 10 个 input
,可能还得费心给 Model.Ints
初始化并添加 10 个 元素。
然而前面的写法真的会报错吗?
其实并不会,it works well.
为什么呢? @Html.TextBoxFor(model => model.Ints[i])
在 Model.Ints
并未初始化的时候就使用了,那么应该会抛出异常才对?
原因
那么,我们来看看为什么没有报错。
这就要从源代码上找原因。
幸好,ASP.NET MVC 已经在 Github 上开源了,地址在这里
所以整个流程中并不会因为 Model.Ints
未初始化而报错,因为 Html.TextBoxFor(model => model.Ints[i])
并不是通过直接访问而是从 expression 数据结构和 ModelState 数据绑定中取值。虽然这背后机制并不复杂,但是这个问题突然冒出来的时候,没有完整看过这部分实现,我也并没有想到这其中的关联。
最后,在使用之前初始化一定是一个好习惯!
附送
其实比起看源码,通过 Visual Studio 来 debug 可能更方便。
那么步骤如下:
- 找到 Tool -> Options -> Debugging -> General
- Uncheck
Enable Just My Code
- Check
Enable Source Server Support
- 转到 Tool -> Options -> Debugging -> Symbols
- Check
Microsoft Symbol Servers
- Add
http://referencesource.microsoft.com/symbols
- Add
http://msdl.microsoft.com/download/symbols
- Add
http://srv.symbolsource.org/pdb/Public
- 我也不知道哪个 symbol server 对你有效,所以就都加上吧~
- 如果你只需要一部分的 modules,可以选择
Only specified modules
,比如添加 System.Web.Mvc.dll
接下来进入调试时,只要右键在当前断点上选择 Step Into Specific
就可以选择进入调试源码了~