由于Oxite1在ASP.NET MVC1还未发布的时候就早早地发布了,2009年2月15日发布的Oxite1口碑不好,但是即将发布的经过重新开发的Oxite2已经不再羸弱了,有非常多的值得观察与学习的地方。本文讨论的内容是基于Oxite2的,你可以在这里下载到最新的Oxite源码:http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx
欢迎加入Oxite小组一起学习:博客园Oxite小组
既然决定学习Oxite2,并且是有源代码的,那就让我们直接从源码开始吧。
解决方案文件
按照这里的说明,打开Oxite解决方案,VS显示整个解决方案共有七个项目组成:
注:上图移除了Oxite.Database项目
可以看出,解决方案中的七个项目中有五个是被分组放在Modules解决方案虚拟文件夹下进行管理的。剩下两个项目,其中Oxite项目是整个解决方案的最底层和核心架构,其他项目均对Oxite项目有引用关系,而另一个项目是Web应用程序项目。我们先大致看一下OxiteSite Web项目的结构,然后适当的时候转入Oxite项目做重点研究。
OxiteSite Web项目
首先展开OxiteSite Web应用程序项目,如下图:
有以下信息值得留意:
1. 整个Web应用程序项目中除View之外无任何Controller,ActionResult,Model,Filter等。
2. 打开Global.asax文件发现只有一行指令。其中Oxite.OxiteApplication类被放在了Oxite项目中。
<%@ Application Inherits="Oxite.OxiteApplication" Language="C#" %>
……
我们看到Oxite的Web应用程序项目只有一个职责:表现数据。这一点值得借鉴。
Oxite类库项目
因为Global.asax中的OxiteApplication类是在Oxite项目中的,所以很自然的我们打开Oxite类库项目看个究竟。通过该项目的命名、目录信息和其他项目对该项目的引用关系等可以判断Oxite类库项目就是整个解决方案的核心项目,该项目构建了Oxite的基础架构,为各个Module项目所用(Module项目是指放在解决方案文件夹Modules下的所有项目,各Module项目的相关信息通过IOxiteModule接口在Application_start中导入Unity容器。用于借助理解,可以设想将来Modules文件夹下可能还会出现Oxite.BBS、Oxite.Xuefly等)。在Oxite核心项目中定义了大量的基类、接口、基础服务、通用服务、以及必要的扩展方法等。其中IOxiteModule接口,Controller工厂OxiteControllerFactory : DefaultControllerFactory,缓存基础架构(ICacheEntity,IOxiteCacheModule),视图引擎IOxiteViewEngine : IViewEngine,插件机制IPluginEngine等,都是在该项目中定义和实现的。一些通用和底层的Model、Controller、自定义ActionResult等也是在这里定义和实现的。
东西很多,不可能做到在一篇随笔中全部包含。现在就让我们针对OxiteApplication : HttpApplication和IOxiteModule来考察一番吧。
OxiteApplication类
很明显画有下滑线的那些方法都是具有特定前缀名的方法,也是我们已经很熟悉的Application的编程接口。其他方法是用于画有下划线的方法的辅助方法。下面一一列举各个方法的作用:如果对相关方法的执行时机和顺序不太了解的话可以查看MSDN
Application_Start()方法:
双击上面类图中的Application_Start()方法或者选中该方法后按下F7进入代码,如下
/// <summary>
/// Setup and load application
/// 设置和加载应用程序
/// </summary>
protected void Application_Start()
{
Application["container"] = setupContainer();
Application["bootStrappersLoaded"] = false;
load();
}
作用见注释,注意到Unity容器被放在了Application["container"]中。
setupContainer()方法:
光标移动至Application_Start()中的setupContainer()调用上,按下F12进入setupContainer()的定义。
在该方法中创建Oxite所使用的Unity容器(IOC)并注册相关数据。,如果你对Unity不熟悉可以参考TerryLee的这篇文章,或者干脆下载Unity的源码和文档,通过看单元测试代码或者阅读仅仅200页的文档来学习也是一个不错的途径。
load()方法:
/// <summary>
/// Initialize any needed info for Oxite.
/// </summary>
private void load()
{
Load(new HttpContextWrapper(Context));
}
Load(HttpContextBase context)方法:
执行所有导入任务以导入各个模块(即实现了IOxiteModule接口的*Module)中注册的信息。注意到一个有趣的接口IBootStrapperTask(Boot Strapper是导入彪形大汉之意,我记得Visual Studio中的某个用于安装.NET的文件夹就是叫这么个名字,.NET团队把.NET比作彪形大汉,估计是寓意.NET很强大和很能干之意)。在Load(HttpContextBase context)方法中同样使用Unity容器取得注册数据。
因为Load方法中的主要逻辑是执行IBootStrapperTask接口的方法,所以我们看一下Unity容器中注册的是什么类型的彪形大汉(IBootStrapperTask)?Application_Start() è setupContainer()找到如下代码:
parentContainer.RegisterInstance<IBootStrapperTask>("LoadModules", new LoadModules(parentContainer));
可以看到,频繁传递的唯一的IUnityContainer实例中注册的是LoadModules : IBootStrapperTask类型的彪形大汉(IBootStrapperTask)。是在LoadModules 中IBootStrapperTask接口的Execute方法中对各个模块(即实现了IOxiteModule接口的*Module)进行循环注册信息到Unity容器的。如果观察一下Modules解决方案文件夹下的其他“模块项目”的话,就会发现Oxite是把各个“模块项目”相关的信息(Route、Filter、Binder、I**Repository、服务、等)分别放在各自项目的“模块(*Module)”中注册的。可以这样认为:所谓IOxiteModule接口其实就是抽象出来用于统一注册信息用的。我们明确定义所谓“模块”就是指继承自IOxiteModule接口的类,减少交流时的概念不统一障碍。
IOxiteModule接口:
public interface IOxiteModule
{
void Initialize();
void Unload();
void RegisterRoutes(RouteCollection routes);
void RegisterCatchAllRoutes(RouteCollection routes);
void RegisterFilters(IFilterRegistry filterRegistry);
void RegisterModelBinders(ModelBinderDictionary modelBinders);
void RegisterWithContainer();
}
Application_BeginRequest方法:
该方法中有两个逻辑
1, 如果尚未安装站点,即ISiteService的GetSite()方法返回空,即数据库中没有站点存在的话,导向安装页。
2, 核实进来的Uri,判断域名是否跟我们存在数据库中的Uri域名信息一致,否则尝试跳转。
另外该方法最后保存了Application_BeginRequest之前,即上次请求的请求上下文信息。
hasSameHostAsRequest和makeHostMatchRequest这两个私有方法的用途很明显就不说了。
OxiteApplication_AcquireRequestState方法:
该方法是Application的AcquireRequestState事件方法,当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时执行。该方法对于交由ASP.NET处理的每次请求都会执行,在该方法的逻辑中对包含当前请求上下文信息HttpContext实例中的IHttpHandler属性值的类型进行了判读,如果当前请求是被MvcHandler处 理程序处理的请求的话,接下来会验证用户状态并保存用户和站点的一些相关信息到当前请求上下文中,否则不做处理(由于IIS7将所有请求交由 ASP.NET处理,所以对于静态图片的请求也是会执行该方法的,但因为默认情况下图片并没有被配置到ASP.NET处理程序,所以不用担心OxiteApplication_AcquireRequestState中的代码会被执行)。
OxiteApplication_AcquireRequestState方法
{
MvcHandler handler = Context.Handler as MvcHandler;
if (handler != null)
{
RequestContext requestContext = handler.RequestContext;
if (requestContext != null)
{
IUnityContainer container = ((IUnityContainer)Application["container"]);
if (container != null)
{
IModulesLoaded modules = container.Resolve<IModulesLoaded>();
if (modules != null)
{
IUser user = new UserAnonymous();
foreach (IOxiteAuthenticationModule module in modules.GetModules<IOxiteAuthenticationModule>().Reverse())
{
user = module.GetUser(requestContext);
if (user.IsAuthenticated)
break;
}
Context.Items[typeof(Site).FullName] = container.Resolve<ISiteService>().GetSite();
Context.Items[typeof(IUser).FullName] = user;
Context.Items[typeof(RequestContext).FullName] = requestContext;
}
}
}
}
}
OxiteApplication类介绍完毕。
建立Oxite小组是 为了通过对Oxite2源码的观察和学习最终得到Oxite开发小组对ASP.NET MVC的一些处理和使用经验。对于ASP.NET MVC我们已经有很多人熟悉了,但可能像我一样仅仅只是做过Demo还没有在实际项目中使用过,我们缺少的正是ASP.NET MVC在实际项目中的使用和处理经验,希望大家通过参与Oxite小组发 现信息、总结经验,甚至形成模式。人与人之间的高效交流需要具备一个统一的知识载体,对于我们各自的ASP.NET MVC知识来说现在正是没有这样一个载体,所以才会交流困难,难以协作,存在误解。怎么办?好办,找个项目作为个体知识的共同载体吧!Oxite2挺好 ^_^,我觉得,你觉得呢?