由于项目需要,刚才打算为ASP.NET MVC应用程序增强ControllerFactory的功能,因此翻出了
ASP.NET MVC的源代码开始阅读其DefaultControllerFactory。代码不多,很容易理解,不过读着读着便发现了问题,因为我发现DefaultControllerFactory不是线程安全的。
线程安全,故名思义便是在多线程的环境下,是否可以正常工作的意思。以前也看过ASP.NET MVC的源代码,印象中在默认情况下,整个应用程序会共享同一个DefaultControllerFactory实例(刚才又确认了一下的确是这样的),这便要求这个类的所有操作——至少是对外公开的接口都需要是线程安全的。
线程安全与否有时候并不容易发现,但是某些状况下我们还是可以总结出一些“
实现模式”,遵循或违反这些模式,则这个组件很可能不是线程安全的。例如,如果满足了Share Nothing,至少是No Shared Writing,这个组件很可能就是线程安全的。不过DefaultControllerFactory可不是这样,例如它的CreateController方法:
- public virtual IController CreateController(RequestContext requestContext, string controllerName) {
- if (requestContext == null) {
- throw new ArgumentNullException("requestContext");
- }
- if (String.IsNullOrEmpty(controllerName)) {
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
- }
- RequestContext = requestContext;
- Type controllerType = GetControllerType(controllerName);
- IController controller = GetControllerInstance(controllerType);
- return controller;
- }
复制代码请看这行代码RequestContext = requestContext;这是一句为自身实例属性RequestContent赋值的语句,它自然是要被接下来的GetControllerType和GetControllerInstance方法所访问。当我们发现了类似的“写属性”的语句应该有所警觉,因为它往往意味着这个组件不是线程安全的。如果是线程安全的代码,则往往需要将数据统统作为方法的参数进行传递。
那么,既然DefaultControllerFactory不是线程安全的,那么为什么在使用过程中却没有发生任何问题呢?幸运的是,在普通使用过程中,我们几乎无法遇到这样的逻辑。例如在GetControllerType中,对RequestContext属性读取的逻辑是这样的:
- protected internal virtual Type GetControllerType(string controllerName) {
- ...
- // first search in the current route's namespace collection
- object routeNamespacesObj;
- Type match;
- if (RequestContext != null &&
- RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
- ...
- }
- ...
- }
复制代码RequestContext永远不可能为null,而且我也从来没有见过谁向RouteData中的DataTokens集合中填充过数据(这需要在应用程序一开始进行Route Mapping时指定),因此这段逻辑“永远”是略过的。GetControllerInstance方法情况略有不同:
- protected internal virtual IController GetControllerInstance(Type controllerType) {
- if (controllerType == null) {
- throw new HttpException(404,
- String.Format(
- CultureInfo.CurrentUICulture,
- MvcResources.DefaultControllerFactory_NoControllerFound,
- RequestContext.HttpContext.Request.Path));
- }
- ...
- }
复制代码只有在ControllerType为null的时候,为了抛出一个404异常才会涉及到ReqeustContext属性——只是,这时候又有谁去注意这个呢?