概述
Unity 应用程序块(Unity)是一个轻量级、可扩展的依赖注入容器,支持构造函数、属性和方法调用注入。它为开发人员提供了如下好处:
简化了对象的创建,尤其是分层的对象结构和依赖。
允许开发人员在运行时或者配置中指定依赖的需求抽象,以及简化了横切关注点的管理。
服务定位功能允许客户代码保存或者缓存容器。这在开发人员可以持久化容器到 ASP.NET Session 或者 Application 中的 ASP.NET Web 应用程序中特别有用。常见场景
除了单独解决横切关注点如日志、认证、授权、缓存和异常处理的组件以外,现代业务系统通常由定制的业务对象和在应用程序中完成特殊的或者一般的任务的组件组成。
成功构建这样的应用程序的关键是获得解耦的或者极度松耦合的设计。松耦合的应用程序更加灵活、更加易于维护。同时在开发期间进行测试,可以模拟对象的桩(轻量级模拟的实现),这增强了实质的依赖。例如,数据库连接、网络连接、ERP 连接和富用户接口组件。
依赖注入是一种用于构建松耦合应用程序的主要技术。它提供了处理对象间依赖的方法。例如,一个处理用户信息的对象可能依赖于访问数据存储、验证信息和检查用户是否被授权执行更新的其他对象。依赖注入技术可以确保用户类正确的初始化及组装所有这些对象,特别是依赖是抽象的地方。
使用容器可以有很多好处,但它会改变应用程序的设计方式,尤其适合于基于组件的开发,朋友们可以有选择的使用它准备相关代码
为了接下来的说明,我们先编写几个后面需要的接口和类:
接口ILogger
public interface ILogger { void Write(string message); }FlatFileLogger类
public class FlatFileLogger : ILogger { public void Write(string message) { Console.WriteLine(String.Format("Message:{0}", message)); Console.WriteLine("Target:FlatFile"); } }public class DatabaseLogger : ILogger { public void Write(string message) { Console.WriteLine(String.Format("Message:{0}",message)); Console.WriteLine("Target:Database"); } }创建容器
在Unity中创建容器实例有两种方法,一是直接使用构造函数创建,如下代码所示:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); } }第二种通过父容器创建容器,在Unity中提供了层级容器的创建,即可以通过父容器来逐级创建容器:
通过父容器来创建容器可以使用CreateChildContainer:
class Program { static void Main(string[] args) { UnityContainer parentContainer = new UnityContainer(); UnityContainer childContainer = (UnityContainer )parentContainer.CreateChildContainer(); } }采用层级容器的好处是我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。class Program { static void Main(string[] args) { UnityContainer parentContainer = new UnityContainer(); UnityContainer childContainer = (UnityContainer )parentContainer.CreateChildContainer(); // can use both generated objects here // Dispose child container childContainer.Dispose(); // can use only object in parent container here // Dispose parent container parentContainer.Dispose(); } }注册接口映射
在Unity中提供了RegisterType方法供我们在容器中注册接口映射:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, DatabaseLogger>(); container.RegisterType<ILogger, FlatFileLogger>("flatfileLogger"); } }第一个泛型参数是基类型,第二个泛型参数是组件类型,它们之间必须满足一定的泛型约束关系:
IUnityContainer RegisterType<TFrom, TTo>() where TTo : TFrom;另外,如果在注册组件时没有指定我们也可以使用非泛型的RegisterType方法进行接口映射:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer();container.RegisterType(typeof(ILogger),typeof(DatabaseLogger)); container.RegisterType(typeof(ILogger),typeof(FlatFileLogger),"flatfileLogger"); } }获取对象实例
在Unity中提供了Get方法用以获取对象实例,如下代码所示:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, FlatFileLogger>(); ILogger logger = container.Resolve<ILogger>(); logger.Write("TerryLee"); } }在上面代码中,我们在容器中注册了一个ILogger接口到FlatFileLogger的映射,当调用Resolve<ILogger>()方法时,将返回ILogger的一个默认的类型实例。
在之前注册接口映射部分,Register方法有一个重载是为接口映射指定一个特定的名称,这样我们可以根据名称和接口来获取一个特定类型的对象实例,如下面的代码我们同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并未DatabaseLogger指定一个名称,在使用Resolve方法的时候就可以通过ILogger和指定的名称来获取DatabaseLogger实例:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, FlatFileLogger>(); container.RegisterType<ILogger, DatabaseLogger>("databaseLogger"); ILogger logger = container.Resolve<ILogger>("databaseLogger"); logger.Write("TerryLee"); } }再运行代码,可以看到获取到了DatabaseLogger实例
如果我们同时同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并且不指定任何名称,那么直接调用Resolve方法将会返回后注册的组件的对象实例,如下面的代码:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, FlatFileLogger>(); container.RegisterType<ILogger, DatabaseLogger>(); ILogger logger = container.Resolve<ILogger>(); logger.Write("TerryLee"); } }运行后会返回DatabaseLogger实例:
当然我们也可以使用非泛型的Resolve方法来获取对象实例:
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, FlatFileLogger>(); ILogger logger = container.Get(typeof(ILogger)) as ILogger; logger.Write("TerryLee"); } }获取所有对象实例
除了可以获取单个对象实例之外,我们还可以一次获取容器中所有与某一接口映射的所有对象实例,但是需要依赖于在注册映射时提供的名称,如果没有指定名称,通过ResolveAll方法不会被获取到。
class Program { static void Main(string[] args) { IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, FlatFileLogger>(); container.RegisterType<ILogger, FlatFileLogger>("flatFileLogger"); container.RegisterType<ILogger, DatabaseLogger>("DatabaseLogger");IEnumerable<ILogger> loggers = container.ResolveAll<ILogger>(); foreach (ILogger logger in loggers) { if (null != logger) { Console.WriteLine(logger.GetType().ToString()); } } } }运行后如下,第一个没有提供名称的类型实例不会被获取到:
备注:
untity容器可以包含 Container-Configured Registration of Types 抽象接口式
- IUnityContainer myContainer = new UnityContainer();
- myContainer.RegisterType<IMyService, CustomerService>();
- IMyService myServiceInstance = myContainer.Resolve<IMyService>();
也可以在容器中包含 Container-Configured Registration of Existing Object Instances 存在实例
- IUnityContainer myContainer = new UnityContainer();
- LoggingService myExistingObject = new LoggingService();
- myContainer.RegisterInstance<IMyService>(myExistingObject);
- IMyService myServiceInstance = myContainer.Resolve<IMyService>();
灵活性比较突出,对于解耦和程序的松散型提供了非常好的支持。