寂寞如此美麗:脫離Application_Start,讓初始化代碼更優美
這里的“寂寞”指的是將ASP.NET程序中的初始化代碼從Global.asax.cs的Application_Start()方法中,移至單獨的程序集中,并且這個程序集與Web項目的程序集沒有任何來往。比如,初始化代碼所在的程序集叫CNBlogs.BootStrapper,Web項目的程序集叫CNBlogs.Web,在Visual Studio中,這兩個項目之間沒有任何引用關系。
因為低耦合而變得寂寞,代碼卻因此變得更美。在生活中,寂寞可以讓人保持內心的寧靜,可以享受更多思考之美。
這篇文章通過兩種方法讓初始化代碼變得更優美:
1)PreApplicationStartMethod(ASP.NET 4.0的新特性,詳見這里)。
2)Bootstrapper(codeplex上的開源項目,詳見 http://bootstrapper.codeplex.com/)。
使用Application_Start()的場景
先看一下不使用Bootstrapper,直接在Application_Start()進行初始化的示例代碼:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
protected void Application_Start()
{
//MVC的注冊
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//IOC容器的注冊
var container = IoCFactory.Instance.CurrentContainter;
container.RegisterType<IBlogSiteService, FakeBlogSiteService>();
container.RegisterType<IBlogPostService, FakeBlogPostService>();
}
這是一種常用場景,在Application_Start()中完成一些初始化注冊與配置,但這些代碼堆在一起,看著總是不順眼。
當使用了測試驅動開發(TDD)之后,不僅看著不順眼,而且用著也不順手,因為在測試項目中也要進行初始化,但測試項目無法調用Application_Start()方法。
我們當時將這部分初始化代碼移至獨立的程序集(CNBlogs.BootStrapper),就是因為測試所需。獨立出來后,Application_Start()與測試項目都通過調用CNBlogs.BootStrapper.Initializer.Initialize()方法完成初始化。
這樣雖然獨立了,但并不寂寞,也不美麗。因為:
1. Web項目要依賴CNBlogs.BootStrapper;
2. 這些初始化代碼依然堆在一些,只是換了個地方。
解決方法
針對第一個問題 ? 用ASP.NET 4.0的新特性PreApplicationStartMethod來解決。在CNBlogs.Bootstrapper項目的AssemblyInfo.cs添加如下的代碼:
[assembly: PreApplicationStartMethod(typeof(CNBlogs.Bootstrapper.Initializer), "Initialize")]
注:CNBlogs.Bootstrapper.Initializer是靜態類,Initialize中Initializer中的靜態方法。
ASP.NET Runtime會自動在程序集中發現這個定義,并在調用Application_Start()之前執行這里指定的Initialize方法。
需要注意的是,AreaRegistration.RegisterAllAreas();只能放在Application_Start()中執行,否則會報錯:
This method cannot be called during the application's pre-start initialization stage.
針對第二個問題 ? 通過 Bootstrapper 將不同的初始化代碼組織成不同的任務(實現IStartupTask接口),然后通過Bootstrap.Bootstrapper的Fluent API調用這些任務,并且可以指定任務的執行順序。比如,我們有兩個任務IocRegisterTask與RegisterRoutesTask,IocRegisterTask要先執行,運行這兩個任務的代碼如下:
public static class Initializer
{
public static void Initialize()
{
Bootstrap.Bootstrapper.With.StartupTasks()
.UsingThisExecutionOrder(s => s.First<RegisterRoutesTask>().Then<IocRegisterTask>())
.Start();
}
}
IoCRegisterTask的實現代碼如下:
public class IocRegisterTask : IStartupTask
{
#region IStartupTask Members
public void Run()
{
var container = IoCFactory.Instance.CurrentContainter;
container.RegisterType<IBlogSiteService, FakeBlogSiteService>();
container.RegisterType<IBlogPostService, FakeBlogPostService>();
}
public void Reset()
{
}
#endregion
}
RegisterRoutesTask的實現代碼如下:
public class RegisterRoutesTask : IStartupTask
{
#region IStartupTask Members
public void Run()
{
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
public void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public void Reset()
{
}
#endregion
}
小結
代碼之美與文學、繪畫、音樂一樣,沒有最美,只有更美。其中的過程是無止境的,其中的樂趣也是無止境的,當然前提是你真正喜歡它。有人說寫程序是吃青春飯,這絕對是個錯誤的想法!因為文學家、畫家、音樂家沒有吃青春飯的,程序員也一樣!寫到這里,突然想到,當年老的時候,把年輕時候寫的代碼拿出來重構一下,一定會很有意思!
浙公網安備 33010602011771號