寂寞如此美丽:脱离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
}
小结
代码之美与文学、绘画、音乐一样,没有最美,只有更美。其中的过程是无止境的,其中的乐趣也是无止境的,当然前提是你真正喜欢它。有人说写程序是吃青春饭,这绝对是个错误的想法!因为文学家、画家、音乐家没有吃青春饭的,程序员也一样!写到这里,突然想到,当年老的时候,把年轻时候写的代码拿出来重构一下,一定会很有意思!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
2006-11-03 [调查]园子里有哪些朋友在做开源项目