2.2.2. 运行时配置
所谓的运行时配置其实就是通过代码进行各种类型的映射和依赖关系的配置,当然你也可以在运行时根据所处的环境选择不同的依赖配置,这是设计时配置所做不到的;但是同时,运行时配置也缺少了些设计时配置所具有的灵活性。在实际的开发过程中,更多的还是两者结合起来使用。在对灵活性要求不是很高的系统中,如果单纯的只是为了系统各模块之间的解耦,从易用性和可维护性的角度上来看,我个人还是推荐使用运行时配置,毕竟运行时配置具有编译器的支持,相比XML的配置更不容易出错。
2.2.2.1. Fluent Configuration Interface
Unity容器的API提供了一种流畅的配置接口,让我们在使用它的API时能够在一行语句中完成所有需要的配置。比如像下面的这段代码:
IUnityContainer container = new UnityContainer(); container.RegisterType<ISayHello, FatherSayHello>("father"); container.RegisterType<ISayHello, MotherSayHello>("mother"); container.RegisterType<ISayHello, ChildSayHello>("child", new InjectionConstructor(2)); container.RegisterType<ISayHello, FamilySayHello>(new InjectionProperty("FamilyMembers", typeof(ISayHello[]))); |
使用Fluent Interface则可以将代码简化成这样:
IUnityContainer container = new UnityContainer() .RegisterType<ISayHello, FatherSayHello>("father") .RegisterType<ISayHello, MotherSayHello>("mother") .RegisterType<ISayHello, ChildSayHello>("child", new InjectionConstructor(2)) .RegisterType<ISayHello, FamilySayHello>(new InjectionProperty("FamilyMembers", typeof(ISayHello[]))); |
通过这样的使用方式不仅可以避免重复输入“container”,还使开发的过程更加的顺畅。当然这种使用方式并不是强制的,完全看你的喜好。
2.2.2.2. 注册类型映射
相比使用配置文件进行设计时注册,通过代码注册就简单了很多,你可以使用RegisterType的多个重载方法进行类型的映射,如下:
//注册接口对应的默认实例 container.RegisterType<IMyService, CustomerService>(); //注册基类对应的默认实例 container.RegisterType<MyServiceBase, DataService>(); //为接口注册有名字的实例(可以通过这种方式为一个接口注册多个实例,按照需要使用) container.RegisterType<IMyService, CustomerService>("Customers"); //使用生命周期管理器注册实例 container.RegisterType<IMyService, CustomerService>(new ContainerControlledLifetimeManager()); |
2.2.2.3. 注册类型实例
使用RegisterInstance方法的多个重载我们可以向容器中注册一个已有的实例。当我们需要容器帮助管理这个对象的生命周期,或者需要向其它对象中注入这个对象的实例时非常的有用。
实例的注册与类型的注册非常相似,唯一有所区别的是,实例注册时需要传入已有的实例。如下:
EmailService myEmailService = new EmailService(); container.RegisterInstance<EmailService>(myEmailService); container.RegisterInstance<EmailService>("Email", myEmailService); container.RegisterInstance<IMyService>(myEmailService, new ExternallyControlledLifetimeManager()); |
2.2.2.4. 依赖注入配置
在前面的设计时配置中已经介绍过,Unity容器的依赖注入配置主要包括构造函数注入、属性注入和方法调用注入三种。这些注入方式都可以通过在RegisterType时传入不同的注入参数(InjectionMember)而实现。
l InjectionConstructor:跟使用配置文件一样,使用这个参数可以对构造函数中的各个参数值进行指定。除了在RegisterType时进行构造函数注入,还可以通过[InjectionConstructor]属性来标识要调用的构造函数。示例代码如下:
IUnityContainer container = new UnityContainer().RegisterType<ILogger, TraceSourceLogger>(new InjectionConstructor(someInt, someString, someDouble)); [InjectionConstructor] public TraceSourceLogger(TraceSource traceSource) { this.traceSource = traceSource; } |
Tip: InjectionConstructor()的优先级高于[InjectionConstructor]属性标识;
l InjectionProperty:使用属性注入可以指明要注入的值,也可以只指明要注入的属性名字,这样容器会使用注册的默认实例进行注入,如果没有注册,那自然就抛出异常。示例代码如下:
container.RegisterType<DriveController>(new InjectionProperty("MyProperty")); int expectedInt = 8; container.RegisterType<DriveController>(new InjectionProperty("IntProperty", expectedInt)); |
与构造函数注入相同,属性注入也可以通过给属性加上[Dependency]属性来实现,示例代码如下:
private ILogger logger; [Dependency] //[Dependency("optionName")] public ILogger Logger { get { return logger; } set { logger = value; } } |
l InjectionMethod:方法调用注入比构造函数注入多增加了一个要注入的方法名,下面的示例中注入了一个名为“InitializeMe”的方法,并传入了两个参数,其中第一个参数是double类型的42.0,另一个是ILogger类型的,这里使用ResolvedParameter类型,告诉容器需要根据事先的配置在运行时获取名为“SpecialLogger”的类型实例来进行参数注入。
container.RegisterType<DriveController>( new InjectionMethod("InitializeMe", 42.0, new ResolvedParameter(typeof(ILogger), "SpecialLogger"))); |
Tip: ResolvedParameter参数也可以在构造函数注入和属性注入中使用。