什么是Unity?
@H_502_0@Unity是一个轻量级的可扩展的依赖注入容器,支持构造函数,属性和方法调用注入。Unity可以处理那些从事基于组件的软件工程的开发人员所面对的问题。构建一个成功应用程序的关键是实现非常松散的耦合设计。松散耦合的应用程序更灵活,更易于维护。这样的程序也更容易在开发期间进行测试。你可以模拟对象,具有较强的具体依赖关系的垫片(轻量级模拟实现),如数据库连接,网络连接,ERP连接,和丰富的用户界面组件。例如,处理客户信息的对象可能依赖于其他对象访问的数据存储,验证信息,并检查该用户是否被授权执行更新。依赖注入技术,可确保客户类正确实例化和填充所有这些对象,尤其是在依赖可能是抽象的 。如何得到Unity?
@H_502_0@您可以访问http://unity.codeplex.com/releases得到最新版本的Unity现在。当然,如果您在您的visual studio 中安装了Nuget 包管理器,你可以直接在Nuget中获取到最新版本的Unity。API
@H_502_0@UnityContainer.RegisterType<ITFrom,TTO>(); @H_502_0@UnityContainer.RegisterType< ITFrom,TTO >(); @H_502_0@UnityContainer.RegisterType< ITFrom,TTO >("keyName"); @H_502_0@IEnumerable<T> databases = UnityContainer.ResolveAll<T>(); @H_502_0@IT instance = UnityContainer.Resolve<IT>(); @H_502_0@T instance = UnityContainer.Resolve<T>("keyName"); @H_502_0@UnitContainer.RegisterInstance<T>("keyName",new T()); @H_502_0@UnityContainer.BuildUp(existingInstance); @H_502_0@IUnityContainer childContainer1 = parentContainer.CreateChildContainer();代码举例
@H_502_0@在开始之前我们要先做一些准备工作。首先创建一个控制台应用程序。使用Nuget 添加Unity到当前项目中。我们可以发现,dll引用中多了3个dll:Microsoft.Practices.ServiceLocation,Microsoft.Practices.Unity和Microsoft.Practices.Configuation。示例1:根据接口依赖创建类
@H_502_0@上边简单介绍了Unity的API。如果在没有注册的情况下Resolve一个类型会发生什么呢? @H_502_0@假设我们需要对日志进行处理。我们先声明一个接口ILogger: @H_502_0@我们可以有多种方法实现这个接口,我们假设希望写日志到文件中:@H_502_65@public @H_502_65@class FileLogger:ILogger { @H_502_65@#region ILogger Members @H_502_65@public @H_502_65@void Write(@H_502_65@string log) { Console.WriteLine("Write log in file."); } @H_502_65@#endregion }
@H_502_65@public @H_502_65@class CustomerDatabase : Database { @H_502_65@private ILogger _logger; @H_502_65@public CustomerDatabase(ILogger logger) { _logger = logger; } }
UnityContainer container = @H_502_65@new UnityContainer();
container.RegisterType<ILogger,FileLogger>();
Database database = container.Resolve<CustomerDatabase>();
示例2:类型映射
@H_502_0@我们希望返回一个日志类的实例,无论它是哪个实现类。我们可以直接在Resolve的类型中指定类型为接口ILogger:UnityContainer container = @H_502_65@new UnityContainer(); container.RegisterType<ILogger,FileLogger>(); ILogger logger = container.Resolve<ILogger>();
示例3:单例模式的注册
@H_502_0@如果我们想告诉Unity,我们想控制生命周期,我们想用单例模式。RegisterType方法包含一个重载,将使用LifetimeManager。每次我们在想获得到database实例时,unity总是会返回第一次我创建的CustomerDatabase。UnityContainer container = @H_502_65@new UnityContainer(); container.RegisterType<Database,CustomerDatabase> (@H_502_65@new ContainerControlledLifetimeManager());
示例4:注册时附带key
@H_502_0@在我们向容器里注册时,可以附带一个string 类型的Key值。UnityContainer container = @H_502_65@new UnityContainer(); container.RegisterType<Database,sqlDatabase>("sql"); container.RegisterType<Database,ORACLEDatabase>("ORACLE"); IEnumerable<Database> databases = container.ResolveAll<Database>(); Database database = container.Resolve<Database>("sql");
示例5:注册已经存在的实例
@H_502_0@通过下边方式我们可以在container中注册一个实例:UnityContainer container = @H_502_65@new UnityContainer(); container.RegisterInstance<Database>(@H_502_65@new sqlDatabase()); container.RegisterInstance<Database>("Oracle",@H_502_65@new ORACLEDatabase()); Database database = container.Resolve<Database>(); Database oracleDatabase = container.Resolve<Database>("Oracle");
UnityContainer container = @H_502_65@new UnityContainer(); container.RegisterType<ILogger,FileLogger>(); sqlDatabase existDatabase = @H_502_65@new sqlDatabase(); container.BuildUp(existDatabase); container.RegisterInstance<Database>(existDatabase); Database database = container.Resolve<Database>();
@H_502_65@public @H_502_65@class DB2Database:Database { [Dependency] @H_502_65@public ILogger Logger { @H_502_65@get; @H_502_65@set; } }
使用配置文件来实现关系映射
@H_502_0@我们也可以再web.config里配置 文件的依赖关系映射。首先我打开web.config文件。按照如下结构添加section。这里我只是简单的映射了ILogger 接口和FileLogger。并且指定了生命周期是单例。@H_502_65@<configuration@H_502_65@> @H_502_65@<configSections@H_502_65@> @H_502_65@<section name@H_502_65@="unity" type@H_502_65@="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"@H_502_65@/> @H_502_65@</configSections@H_502_65@> @H_502_65@<unity xmlns@H_502_65@="http://schemas.microsoft.com/practices/2010/unity"@H_502_65@> @H_502_65@<container name@H_502_65@="containerOne"@H_502_65@> @H_502_65@<types@H_502_65@> @H_502_65@<type type@H_502_65@="UnityDemo_ConsoleApplication.ILogger" mapTo@H_502_65@="UnityDemo_ConsoleApplication.FileLogger" lifeTime@H_502_65@="Singleton"@H_502_65@/> @H_502_65@</types@H_502_65@> @H_502_65@</container@H_502_65@> @H_502_65@</unity@H_502_65@> ... ... @H_502_65@</configuration@H_502_65@>
@H_502_0@更详细了解,请参见: @H_502_0@http://msdn.microsoft.com/en-us/library/ff647848.aspx @H_502_0@http://msdn.microsoft.com/zh-cn/library/dd203230.aspx
如何读取配置 并加载
@H_502_0@Unity同样支持我们在配置文件里写设定映射关系。 @H_502_0@首先我们要引入命名空间: Microsoft.Practices.Unity.Configuration; @H_502_0@在Unity2.0以上版本,已经废弃了以前的方法。现在我们有2种方式可以读取配置。 @H_502_0@第一种,我们使用configurationManager: @H_502_0@引用命名空间:System.ConfigurationIUnityContainer myContainer = new UnityContainer(); myContainer.LoadConfiguration("containerOne "); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); section.Configure(myContainer,"containerOne");
IUnityContainer myContainer = new UnityContainer();
myContainer.LoadConfiguration("containerName");
通过 Injection API 指定依赖
@H_502_0@假设我们有一个类GenericDatabase: @H_502_0@public class GenericDatabase:Database { private string _connectionString; public ILogger Logger { get; set; } public GenericDatabase(string connectionString) { _connectionString = connectionString; } }
IUnityContainer container = new UnityContainer(); container.RegisterType@H_502_65@<ILogger,FileLogger@H_502_65@>(); container.RegisterType@H_502_65@<Database,GenericDatabase@H_502_65@>();
container.Configure@H_502_65@<InjectedMembers@H_502_65@>() .ConfigureInjectionFor@H_502_65@<GenericDatabase@H_502_65@>( new InjectionConstructor( ConfigurationManager.ConnectionStrings["ConnectionStrings"] == null ? "defaultConnectionString" : ConfigurationManager.ConnectionStrings["ConnectionStrings"].ConnectionString),new InjectionProperty("Logger") ); Database database = container.Resolve@H_502_65@<Database@H_502_65@>();
嵌套式容器
@H_502_0@容器是可以嵌套的,获取实例时遵循的规则是,如果子容器里不包含需要的对象,则会去父容器获取。如果有,则从自己里获取。 @H_502_0@一旦父容器销毁,子容器也随之销毁。UnityContainer parentContainer = new UnityContainer(); IUnityContainer childContainer1 = parentContainer.CreateChildContainer(); IUnityContainer childContainer2 = parentContainer.CreateChildContainer(); parentContainer.RegisterType@H_502_65@<ILogger,FileLogger@H_502_65@>(new ContainerControlledLifetimeManager()); childContainer1.RegisterType@H_502_65@<ILogger,EventLogger@H_502_65@>(new ContainerControlledLifetimeManager()); //应该从parentContainer得到FileLogger ILogger logger = childContainer2.Resolve@H_502_65@<ILogger@H_502_65@>(); logger.Write("Test"); //应该从自己本身得到eventLogger ILogger logger2 = childContainer1.Resolve@H_502_65@<ILogger@H_502_65@>();
在MVC 中使用Unity注入Controller
@H_502_0@在MVC2中我们会写一个controlleFactory 继承自DefaultControllerFactory。 @H_502_0@并且override GetControllerInstance()这个方法。 @H_502_0@MVC3对于依赖注入提供更好的支持。我们可以使用- IDependencyResolver 和 IControllerActivator 来实现对controller的注入。 @H_502_0@具体实现如下: @H_502_0@创建一个MVC3项目。 @H_502_0@我们要实现MVC3中新提供 的两个接口:IDependencyResolver和IControllerActivator @H_502_0@IDependencyResolver公开两个方法 - GetService的GetServices.The GetService方法解决了单独注册的服务,支持任意对象的创建,GetServices解决注册多个服务。IDependencyResolver接口的实现应该委托给底层的依赖注入容器提供注册服务请求的类型。当有没有注册的服务请求的类型,ASP.NET MVC框架预计这个接口的实现返回GetService为空,并从GetServices返回空集合。让我们以统一提供依赖注入工作IDependencyResolver intreface派生创建一个自定义的依赖解析器类。 @H_502_0@我们定义一个类名为UnityDependencyResolver:@H_502_65@public @H_502_65@class UnityDependencyResolver : IDependencyResolver { IUnityContainer container; @H_502_65@public UnityDependencyResolver(IUnityContainer container) { @H_502_65@this.container = container; } @H_502_65@public @H_502_65@object GetService(Type serviceType) { @H_502_65@try { @H_502_65@return container.Resolve(serviceType); } @H_502_65@catch { @H_502_65@return @H_502_65@null; } } @H_502_65@public IEnumerable<@H_502_65@object> GetServices(Type serviceType) { @H_502_65@try { @H_502_65@return container.ResolveAll(serviceType); } @H_502_65@catch { @H_502_65@return @H_502_65@new List<@H_502_65@object>(); } } }
IController IControllerActivator.Create( System.Web.Routing.RequestContext requestContext,Type controllerType) { @H_502_65@return DependencyResolver.Current .GetService(controllerType) @H_502_65@as IController; }
@H_502_65@private IUnityContainer GetUnityContainer() { //Create UnityContainer IUnityContainer container = @H_502_65@new UnityContainer() .RegisterType<IControllerActivator,CustomControllerActivator>() .RegisterType<ILogger,FlatFileLogger>(); @H_502_65@return container; }
IUnityContainer container = GetUnityContainer(); DependencyResolver.SetResolver(@H_502_65@new UnityDependencyResolver(container));
@H_502_65@protected @H_502_65@void Application_Start() { ... IUnityContainer container = GetUnityContainer(); DependencyResolver.SetResolver(@H_502_65@new UnityDependencyResolver(container)); }
@H_502_65@public @H_502_65@class HomeController : Controller { [Dependency] @H_502_65@public ILogger Logger { @H_502_65@get; @H_502_65@set; } @H_502_65@public ActionResult Index() { ViewBag.Message = "Welcome to ASP.NET MVC!"; Logger.GetType(); @H_502_65@return View(); } @H_502_65@public ActionResult About() { @H_502_65@return View(); } }