在使用 Ioc 框架时,一般我们建议集中在一个称为 Composition Root(其含义请参见下面的小注)的位置来注册 (Register) 和解析 (Resolve) 服务。这种做法的目的在于限制 Ioc 的使用场合,防止在应用程序中到处引用 Ioc 框架,从而尽量减少应用程序本身对于 Ioc 框架的依赖。
上述模式固然能够很好地解耦应用程序和 Ioc 框架,使我们能够在需要的时候方便地更换 Ioc 框架,但它同时也带来了一个问题:难道我们一定要在程序启动时注册所有服务吗?有些服务并不一定会马上用到,有一些服务甚至在整个应用程序运行过程中都可能不会用到。在这种情况下,如果仍然固守上述模式,在程序启动时将所有服务都注册到容器中,其结果无疑会减缓程序启动速度,同时增加内存消耗。
针对这个问题,我们在 My.Ioc 中给出了延迟注册的解决办法。它的原理其实很简单:当应用程序向 My.Ioc 容器请求某个服务时,如果此时该服务尚未注册,容器便会触发一个 ObjectBuilderRequested 事件,并在事件参数中提供所请求的契约类型 (Contract Type) 和注入点信息 (InjectionInfo),而订阅了该事件的处理程序 (Event Handler) 便可以在这个时候根据契约类型和注入点信息来决定是否向容器中注册相应的服务以满足应用程序需求。
用法很简单,示例代码如下:
using System; using System.Diagnostics; using My.Ioc; namespace LazyRegistration { public interface ILazyService { } public class LazyService : ILazyService { } class Program { static bool _eventHandled = false; static void Main(string[] args) { IObjectContainer container = new ObjectContainer(false); container.ObjectBuilderRequested += OnObjectBuilderRequested; var lazy = container.Resolve<ILazyService>(); Debug.Assert(lazy != null); Debug.Assert(lazy is LazyService); Console.WriteLine(_eventHandled); Console.ReadLine(); } static void OnObjectBuilderRequested(ObjectBuilderRequestedEventArgs args) { if (args.ContractType == typeof (ILazyService)) { args.Register<ILazyService, LazyService>(); _eventHandled = true; } } } }
小注:
Composition Root 的定义是“一个组装应用程序各个模块的位置,一个应用程序中应该仅有一个这样的位置,这个位置应当尽量靠近应用程序入口点,在一般情况下它就是应用程序的入口点。例如,对于控制台应用程序来说,这个位置可能就是 Main 函数;对于 ASP.NET MVC 应用程序来说,这个位置可能是 global.asax;对于 WPF 应用程序来说,这个位置可能是 App.xaml...”。
源码可从此处下载。