Robin's Blog

记录 积累 学习 成长

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

依赖注入容器Unity Application Block快速入门

概述

Unity是微软模式与实践团队开发的一个轻量级、可扩展的依赖注入容器,之前我也有过一篇文章《Enterprise Library 4.0中的依赖注入容器(Unity)预览》对其做过介绍。微软模式与时间团队已经在2月份发布了Unity February 2008 CTP版本,官方主页是:http://www.codeplex.com/unity,大家可以到网站上去下载相关的源代码。本文将通过一些示例让您对Unity使用有一个逐步的认识和了解。

准备相关代码

为了接下来的说明,我们先编写几个后面需要的接口和类:

接口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");
    }
}

DatabaseLogger类

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中提供了层级容器的创建,即可以通过父容器来逐级创建容器:

Unity_TerryLee_001

图1 Unity中层级容器

通过父容器来创建容器可以使用CreateChildContainer:

class Program
{
    static void Main(string[] args)
    {
        UnityContainer parentContainer = new UnityContainer();

        UnityContainer childContainer = parentContainer.CreateChildContainer();
    }
}

采用层级容器的好处是我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。

class Program
{
    static void Main(string[] args)
    {
        UnityContainer parentContainer = new UnityContainer();

        UnityContainer childContainer = 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中提供了Register方法供我们在容器中注册接口映射:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, DatabaseLogger>();
        container.Register<ILogger, FlatFileLogger>("flatfileLogger");
    }
}

第一个泛型参数是基类型,第二个泛型参数是组件类型,它们之间必须满足一定的泛型约束关系:

IUnityContainer Register<TFrom, TTo>() where TTo : TFrom;

另外,如果在注册组件时没有指定我们也可以使用非泛型的Register方法进行接口映射:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
        container.Register(typeof(ILogger),typeof(DatabaseLogger));
        container.Register(typeof(ILogger),typeof(FlatFileLogger),"flatfileLogger");
    }
}

获取对象实例

在Unity中提供了Get方法用以获取对象实例,如下代码所示:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();

        ILogger logger =  container.Get<ILogger>();

        logger.Write("TerryLee");
    }
}

在上面代码中,我们在容器中注册了一个ILogger接口到FlatFileLogger的映射,当调用Get<ILogger>()方法时,将返回ILogger的一个默认的类型实例。运行代码可以看到:

Unity_TerryLee_002

在之前注册接口映射部分,Register方法有一个重载是为接口映射指定一个特定的名称,这样我们可以根据名称和接口来获取一个特定类型的对象实例,如下面的代码我们同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并未DatabaseLogger指定一个名称,在使用Get方法的时候就可以通过ILogger和指定的名称来获取DatabaseLogger实例:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, DatabaseLogger>("databaseLogger");

        ILogger logger = container.Get<ILogger>("databaseLogger");

        logger.Write("TerryLee");
    }
}

再运行代码,可以看到获取到了DatabaseLogger实例:

Unity_TerryLee_003

如果我们同时同时注册FlatFileLogger和DatabaseLogger到接口ILogger的映射,并且不指定任何名称,那么直接调用Get方法将会返回后注册的组件的对象实例,如下面的代码:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, DatabaseLogger>();

        ILogger logger = container.Get<ILogger>();

        logger.Write("TerryLee");
    }
}

运行后会返回DatabaseLogger实例:

Unity_TerryLee_003

当然我们也可以使用非泛型的Get方法来获取对象实例:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();

        ILogger logger = container.Get(typeof(ILogger)) as ILogger;

        logger.Write("TerryLee");
    }
}

获取所有对象实例

除了可以获取单个对象实例之外,我们还可以一次获取容器中所有与某一接口映射的所有对象实例,但是需要依赖于在注册映射时提供的名称,如果没有指定名称,通过GetAll方法不会被获取到。

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.Register<ILogger, FlatFileLogger>();
        container.Register<ILogger, FlatFileLogger>("flatFileLogger");
        container.Register<ILogger, DatabaseLogger>("DatabaseLogger");
        IEnumerable<ILogger> loggers = container.GetAll<ILogger>();

        foreach (ILogger logger in loggers)
        {
            if (null != logger)
            {
                Console.WriteLine(logger.GetType().ToString());
            }
        }
    }
}
运行后如下,第一个没有提供名称的类型实例不会被获取到:
Unity_TerryLee_004

结束语

关于Unity的一个简单使用就到这里了,希望能够对大家有用。使用Unity简单来说分为创建容器、注册接口映射、获取对象实例这样三步,在下篇文章中,将会说到如何使用配置文件注册接口映射以及如何注册Singleton实例等内容。

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-02-21 23:35 TerryLee 阅读(8169) 评论(67)  编辑 收藏 网摘 所属分类: [10]  模式与实践[09]  依赖注入

  回复  引用    
#1楼 2008-02-21 23:44 | 小鬼00 [未注册用户]
希望能够对大家游泳
  回复  引用    
#2楼 2008-02-21 23:44 | 小鬼00 [未注册用户]
谢谢
  回复  引用  查看    
#3楼 [楼主]2008-02-21 23:45 | TerryLee      
@小鬼00
笔误,呵呵:)

  回复  引用  查看    
#4楼 [楼主]2008-02-21 23:45 | TerryLee      
@小鬼00
客气了:D

  回复  引用    
#5楼 2008-02-21 23:48 | zhchch518 [未注册用户]
万分感谢,一直想看介绍Unity的文章,想问一下和castle的ioc框架比,Unity的优点在哪里
  回复  引用  查看    
#6楼 [楼主]2008-02-21 23:56 | TerryLee      
@zhchch518
现在Unity还处在CTP阶段,只能算是一个IOC框架的原型,还不具有什么优势:)

  回复  引用  查看    
#7楼 2008-02-22 00:08 | 无常      
跟着学习
  回复  引用  查看    
#8楼 2008-02-22 00:38 | Adrian H.      
期待后面的精彩内容,谢谢Terry:))
  回复  引用  查看    
#9楼 2008-02-22 00:48 | xingd      
Entlib 4.0似乎说是2月底发布的,不知道有没有更明确的时间了。
  回复  引用    
#10楼 2008-02-22 04:19 | 台州 [未注册用户]
不错 学习了 希望以后更精彩
  回复  引用  查看    
#11楼 [楼主]2008-02-22 08:18 | TerryLee      
@无常
:)

  回复  引用  查看    
#12楼 [楼主]2008-02-22 08:18 | TerryLee      
@Adrian H.
客气了,呵呵:)

  回复  引用  查看    
#13楼 [楼主]2008-02-22 08:19 | TerryLee      
@xingd
目前还没有看到发布的具体时间,其实EL4.0除了增加了Unity之外,其他的都是一些小的改动:)

  回复  引用  查看    
#14楼 [楼主]2008-02-22 08:19 | TerryLee      
@台州
我尽量做到:)

  回复  引用  查看    
#15楼 2008-02-22 09:21 | 阿牛 - 专注OOP      
跟着TerryLee 走!^_^
  回复  引用  查看    
#16楼 [楼主]2008-02-22 09:27 | TerryLee      
@阿牛 - 专注OOP
呵呵:)

  回复  引用  查看    
#17楼 2008-02-22 09:42 | Spring.Cheung      
支持,典型的IOC样例
  回复  引用  查看    
#18楼 [楼主]2008-02-22 10:08 | TerryLee      
@Spring.Cheung
呵呵,Unity现在还只是一个IOC的原型吧,后续还有几篇文章对其介绍:)

  回复  引用  查看    
#19楼 2008-02-22 10:53 | Artech      
DI确实"很好,很强大"。
在Software Factory广泛使用到DI,值得好好研究一下^_^

  回复  引用  查看    
#20楼 2008-02-22 11:06 | 戏水      
@TerryLee
在本文中没有发现
写“Target:FlatFile” 的代码
怎么在 图中 就输出了 这个 Target:FlatFile 的字符串呢?

另外 您的文章非常的好 。让咱受益匪浅。 不过对于愚钝浅薄的我来讲。
依赖注入 还是比较模糊的概念。 虽然上面的代码都能看懂 但是依然不能体会 这个东西给开发带来的好处。 希望兄长 以后的文中能顺带提点几句,以助末学,不胜感激。

  回复  引用  查看    
#21楼 2008-02-22 11:10 | xingd      
--引用--------------------------------------------------
TerryLee: @xingd
目前还没有看到发布的具体时间,其实EL4.0除了增加了Unity之外,其他的都是一些小的改动:)
--------------------------------------------------------
http://blogs.msdn.com/agile/archive/2007/12/03/making-dependency-injection-simpler-in-entlib-4.aspx 里,回复的时候提到是at the end of February - March 2008.

想用Entlib 4.0里的IoC,还在等待。

  回复  引用  查看    
#22楼 [楼主]2008-02-22 11:11 | TerryLee      
@戏水
在准备相关代码一节里面,可以看DatabaseLogger和FlatFileLogger类的实现

关于依赖注入已经提出好长时间了,网上也有大量的资料可供参考,以后的文章我会注意这一点:)

  回复  引用  查看    
#23楼 [楼主]2008-02-22 11:12 | TerryLee      
@Artech
嗯,确实是这样的:)

  回复  引用  查看    
#24楼 [楼主]2008-02-22 11:14 | TerryLee      
@xingd
EL4中的IOC不就是Unity吗?最终将以两种方式发布:作为EL4的一部分和作为一个独立的应用程序块。

不知道月底EL4会不会按时发布。。。

  回复  引用  查看    
#25楼 2008-02-22 12:25 | overred      
支持下。。。没事就来学习下。。。
  回复  引用  查看    
#26楼 [楼主]2008-02-22 12:49 | TerryLee      
@overred
:)

  回复  引用  查看    
#27楼 2008-02-22 13:15 | Tony Zhou      
很不错!
  回复  引用  查看    
#28楼 [楼主]2008-02-22 13:18 | TerryLee      
@Tony Zhou
:)

  回复  引用  查看    
#29楼 2008-02-22 14:03 | newr2006      
good
  回复  引用    
#30楼 2008-02-22 16:32 | ifire [未注册用户]
赞美之词我就不重复了,呵呵

泼泼水:要是有深入的东西就好了

  回复  引用    
#31楼 2008-02-22 16:43 | XX0XX [未注册用户]
太棒了 等注入等了好久了
  回复  引用  查看    
#32楼 2008-02-22 20:52 | 皇帝的新装      
思路跟castle一样
  回复  引用  查看    
#33楼 2008-02-23 00:35 | Dorian Deng      
  回复  引用  查看    
#34楼 [楼主]2008-02-23 00:56 | TerryLee      
@newr2006
谢谢:)

  回复  引用  查看    
#35楼 [楼主]2008-02-23 00:57 | TerryLee      
@ifire
这只是第一篇“快速入门”文章,旨在让读者对Unity有一个概要的认识,后续的文章会继续深入

  回复  引用  查看    
#36楼 [楼主]2008-02-23 00:58 | TerryLee      
@XX0XX
呵呵,那就赶快体验一下吧:)

  回复  引用  查看    
#37楼 [楼主]2008-02-23 00:58 | TerryLee      
@皇帝的新装
有些思路与Castle还是有区别的,如属性注入

  回复  引用  查看    
#38楼 [楼主]2008-02-23 00:59 | TerryLee      
@Dorian Deng
^_^

  回复  引用  查看    
#39楼 2008-02-26 13:51 | 一瞬间      
terrylee大哥
借地方问个asp.net MVC的问题,就是Scott那个例子里面的,在ListView里面使用Html.ActionLink来生成的情况,(用内联方法可以)会报编译错误。一直解决不了,如果可以的话能不能帮助我一下,谢谢

  回复  引用  查看    
#40楼 2008-02-26 16:50 | 老钱      
如果FlatFileLogger和 DatabaseLogger构造函数有不同的签名怎么注入呢.?
  回复  引用  查看    
#41楼 [楼主]2008-02-26 18:59 | TerryLee      
@老钱
后续会提到构造函数依赖

  回复  引用  查看    
#42楼 [楼主]2008-02-26 19:00 | TerryLee      
@一瞬间
右边有我的邮箱,发mail给我吧

  回复  引用  查看    
#43楼 2008-06-19 21:46 | weidagang2046      
楼主的多数文章都不错,但这篇文章我要拍一下砖:介绍IoC不用反射,容易误导初学者。"interface + reflection"是IoC的两大要素,少了反射何谈IoC的优点。
  回复  引用  查看    
#44楼 [楼主]2008-06-19 22:00 | TerryLee      
@weidagang2046
这砖拍的有些莫名其妙啊。。。

  回复  引用  查看    
#45楼 2008-06-19 22:30 | weidagang2046      
我自己就是IoC的初学者,起初看了这篇文章,感觉IoC框架(这里就是Unity了)没什么用,因为创建对象还是要依赖于具体的类(这篇文章就是直接引用类名)。后来,看到其他文章介绍IoC用了反射才领会到IoC的作用。
  回复  引用  查看    
#46楼 [楼主]2008-06-19 23:58 | TerryLee      
@weidagang2046
你有些本末倒置了啊,应该是先领会什么是IOC,然后再学习IOC框架。。。

  回复  引用  查看    
#47楼 2008-06-20 00:04 | weidagang2046      
@TerryLee
我是想一起就学了,的确有本末倒置的嫌疑^_^ 但我相信很多人是从技术到原理,比如先听说ASP.NET MVC,才开始去了解MVC。

  回复  引用  查看    
#48楼 [楼主]2008-06-20 08:59 | TerryLee      
@weidagang2046
呵呵,我不认为这是好的学习习惯,对于MVC我相信绝大多数人是先了解的MVC,然后才接触ASP.NET MVC Framework.

  回复  引用  查看    
#49楼 2008-06-24 09:34 | 一瞬间      
第一个没有提供名称的类型实例不会被获取到,这个是什么原因呢?
  回复  引用  查看    
#50楼 [楼主]2008-06-24 12:26 | TerryLee      
@一瞬间
这个是在CTP版下写的代码,现在1.1版本我不知道有没有改动。。。

  回复  引用    
#51楼 2008-06-29 18:53 | hook [未注册用户]
在Program中直接引用了具体的实现类,这显然违背了依赖注入的原理和思想。
有个同学(weidagang2046 )已经提出了问题,但博主的反驳确实搞笑。有问题就是有问题,应该勇于承认。
显然对于IOC容器来说,我们希望看到的是:
container.Register<ILogger>("flatfileLogger"),框架可以通过键值去配置文件里寻找具体的实现,这样我们的UI层才不会依赖于具体的类。
但我们在程序中看到的是:
container.Register<ILogger, FlatFileLogger>("flatfileLogger");
这说明,你的Program直接引用了具体的现实,这当然容易引起别人的误解,别人当然就会认为这个框架没得用了!

  回复  引用  查看    
#52楼 [楼主]2008-06-29 22:27 | TerryLee      
@hook
在任何一个IOC容器中,都会提供有两种方式,代码加入注册组件或者使用配置文件注册组件,有时候我们会根据实际需要选用。本文之所以出现误解,是因为注册组件和从容器中获取组件放在了同一段代码中。
使用下面这种方式意义不是很大:
container.Register<ILogger>("flatfileLogger")

准确的应该是:
container.Register("config.xml");
// 当然我不知道Unity中是否支持这种写法

  回复  引用  查看    
#53楼 2008-08-19 13:48 | 一抹微蓝      
Unity依赖注入是侵入式的吗?
  回复  引用  查看    
#54楼 [楼主]2008-08-20 10:14 | TerryLee      
@一抹微蓝
两种方式都支持的

  回复  引用  查看    
#55楼 2008-11-04 11:02 | ITAres      
跟着terrylee学习,有肉吃
  回复  引用  查看    
#56楼 [楼主]2008-11-05 09:26 | TerryLee      
@ITAres
o(∩_∩)o...

  回复  引用  查看    
#57楼 2008-12-10 21:44 | 边城浪      
不错...通俗易懂..对初学入门很有用.
  回复  引用  查看    
#58楼 [楼主]2008-12-15 10:23 | TerryLee      
@边城浪
对大家有用就好:)

  回复  引用  查看    
#59楼 2009-03-11 22:52 | "呆子"      
如果我又很多个模块,想在不同的地方在同一个容器里面注册要怎么实现呢?
  回复  引用    
#60楼 2009-03-13 08:16 | Qin Liang [未注册用户]
一大早起来,看了TerryLee的这篇关于Unity的文章,感觉挺有意思的。Hi, long time no see, how are you, my friend.
  回复  引用    
#61楼 2009-06-08 13:01 | DENLI [未注册用户]
《如何使用配置文件注册接口映射以及如何注册Singleton实例等内容》
的内容出来了吗?我怎么找不到?
希望能拜读一下!

  回复  引用    
#62楼 2009-06-08 13:02 | DENLI [未注册用户]
《如何使用配置文件注册接口映射以及如何注册Singleton实例等内容》
的内容出来了吗?我怎么找不到?
希望能拜读一下!

  回复  引用  查看    
#63楼 2009-06-21 21:00 | Old      
不错!好文章...
:-)

  回复  引用    
#64楼 2009-06-23 16:22 | 芝芝 [未注册用户]
感谢Terry,看你的文章总是感觉很轻松愉快,不像很多高手英语+术语的说了一顿和没说一样
posted on 2009-06-25 17:56  Robin99  阅读(245)  评论(0编辑  收藏  举报