第08章 ASP.NET 5:依赖注入

1.背景

    如果某个具体的(或类)对象被客户程序所依赖,通常把它们抽象成抽象类或接口。简单说,客户程序摆脱所依赖的具体类型,称之为面向接口编程

    那么问题来了?如何选择客户程序所需要的实现类?在使用创建型模式下创建对象是不难解决这个问题。

    但如果设计的不是具体业务逻辑,而是公共类库或框架程序,对外只提供抽象而已,该如何把外部使用的类型传递给它们?

    我们可以采用“依赖注入”的方式,将加工好的抽象类型实体“注入”到客户程序中。

    注:依赖注入DI(Dependency Injection),它和控制反转IOC(Inversion of Control)是同一个意思,不同术语叫法。

2.场景

    客户程序获取年份,我们先设计一个接口

using System;

namespace Blog.Consoles
{
    public interface ITimeProvider
    {
        DateTime CurrentDate { get; }
    }
}

    并对这个接口实现(可以多种实现哦,这也是设计接口的原因):

using System;

namespace Blog.Consoles
{
    public class TimeProvider : ITimeProvider
    {
        public DateTime CurrentDate
        {
            get { return DateTime.Now; }
        }
    }
}

      那么我们在客户程序使用

using System;

namespace Blog.Consoles
{
    class Program
    {
        static void Main(string[] args)
        {
            ITimeProvider tp = new TimeProvider();
            Console.WriteLine(tp.CurrentDate.Year);

            Console.ReadKey();
        }
    }
}

       这样实现的依赖关系是:

       技术分享

       显然这样客户程序还需要知道具体类型TimeProvider,我们增加一个对象:

using System;
using System.Collections.Generic;

namespace Blog.Consoles
{
    public class Assembler
    {
        //保存抽象类型和实体类型
        static Dictionary<Type, Type> d = new Dictionary<Type, Type>();

        static Assembler()
        {
            //注册抽象类型需要使用的实体类型
            d.Add(typeof(ITimeProvider), typeof(TimeProvider));
        }

        public object Create(Type type)
        {
            if ((type == null) || !d.ContainsKey(type))
            {
                throw new NullReferenceException();
            }

            return Activator.CreateInstance(d[type]);
        }

        public T Create<T>()
        {
            return (T)Create(typeof(T));
        }
    }
}

      此时再改造依赖关系

      技术分享

      这样客户程序只依赖Assembler和ITimeProvider,并不知道TimeProvider存在。

      接下来如何写注入代码?下面分几种方式。

3.构造注入

      构造注入使用构造方法,通过Assembler或其它机制把抽象类型作为参数传递。其实现代码:

using System;

namespace Blog.Consoles
{
    class Program
    {
        public ITimeProvider _tp;
        public Program(ITimeProvider tp)
        {
            _tp = tp;
        }

        public int GetYear()
        {
            return _tp.CurrentDate.Year;
        }

        static void Main(string[] args)
        {
            ITimeProvider time = new Assembler().Create<ITimeProvider>();
            Program p = new Program(time);
            Console.WriteLine(p.GetYear()); 

            Console.ReadKey();
        }
    }
}

4.设置注入

      通过属性方法赋值实现的,相对于构造方法一次性注入的方式,设置注入可以在需要时有更改的机会。实现代码:

using System;

namespace Blog.Consoles
{
    class Program
    {
        public ITimeProvider _tp { get; set; }

        public int GetYear()
        {
            return _tp.CurrentDate.Year;
        }

        static void Main(string[] args)
        {
            ITimeProvider time = new Assembler().Create<ITimeProvider>();

            Program p = new Program();
            p._tp = time;

            Console.WriteLine(p.GetYear());

            Console.ReadKey();
        }
    }
}

       还可以简写: 

using System;

namespace Blog.Consoles
{
    class Program
    {
        public ITimeProvider _tp { get; set; }

        public int GetYear()
        {
            return _tp.CurrentDate.Year;
        }

        static void Main(string[] args)
        {
            var p = new Program() { _tp = new Assembler().Create<ITimeProvider>() };

            Console.WriteLine(p.GetYear());

            Console.ReadKey();
        }
    }
}

5.MEF注入

    导出部件:

    技术分享

    下面不使用Assembler对象,实现代码:

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;

namespace Blog.Consoles
{
    class Program
    {
        private static CompositionContainer container;

        private void Compose()
        {
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            container = new CompositionContainer(catalog);
            //将部件和宿主程序添加到组合容器
            container.ComposeParts(this, new TimeProvider());
        }

        //导入部件
        [Import]
        public ITimeProvider _tp { get; set; }

        public int GetYear()
        {
            return _tp.CurrentDate.Year;
        }

        static void Main(string[] args)
        {
            var p = new Program();
            p.Compose();
            
            Console.WriteLine(p.GetYear());

            Console.ReadKey();
        }
    }
}

 6.ASP.NET 5注入

       修改Startup.cs:

       技术分享

      测试:

      技术分享

7.小结

      关于依赖注入的方式,还是接口注入,自定义特性等(它们之间区别和选择,自行体会或查资料),不常用就不说了。

      感受到ASP.NET 5自带DI爽就行啦!(ASP.NET 5真的海皮啦!)

      后面的帖子,进入项目实战了,最近比较忙,请园友保持耐心。

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。