.Net IOC框架入门之——Unity

一、概述

IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection)。
  作用:将各层的对象以松耦合的方式组织在一起,解耦,各层对象的调用完全面向接口。当系统重构的时候,代码的改写量将大大减少。
  依赖注入: 当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。然而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。

   Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器;

  它支持常用的三种依赖注入方式:构造器注入(Constructor Injection)、属性注入(Property Injection),以及方法调用注入(Method Call Injection)。

二、简单示例

1、定义一个接口,封装数据库的基本CRUD操作,接口定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    /// <summary>
    /// 数据访问接口
    /// </summary>
    public interface IDataBase
    {
        string Insert();
        string Delete();
        string Update();
        string Query();
    }
}

 2、定义一个MSSQL类实现该接口,用来模仿SQLServer操作,MSSQL类定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class DbMSSQL : IDataBase
    {
        public string Delete()
        {
            return "MSSQL执行删除";
        }

        public string Insert()
        {
            return "MSSQL执行插入";
        }

        public string Query()
        {
            return "MSSQL执行查询";
        }

        public string Update()
        {
            return "MSSQL执行更新";
        }
    }
}

3、定义一个Oracle类实现该接口,模仿Oracle的操作,Oracle类定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class DbOracle : IDataBase
    {
        public string Delete()
        {
            return "MSSQL执行删除";
        }

        public string Insert()
        {
            return "MSSQL执行插入";
        }

        public string Query()
        {
            return "MSSQL执行查询";
        }

        public string Update()
        {
            return "MSSQL执行更新";
        }
    }
}

 4、定义一个控制台应用程序来调用:

 // 常规做法,即程序的上端,依赖于下端,依赖于细节
     DbMSSQL mssql = new DbMSSQL();

 // 通过抽象来依赖
    IDataBase dbInterface = new DbMSSQL();

但是这样修改以后,虽然左边是抽象了,但是右边还是依赖于细节。

先来看看最简单的Unity实现方式:

 // 常规做法,即程序的上端,依赖于下端,依赖于细节
            DbMSSQL mssql = new DbMSSQL();
            // 通过抽象来依赖
            IDataBase dbInterface = new DbMSSQL();

            IUnityContainer container = new UnityContainer();// 1. 定义一个空容器
            container.RegisterType<IDataBase, DbMSSQL>();// 2.注册类型,表示遇到IDataBase,创建DbMSSQL的实例
            var db = container.Resolve<IDataBase>();
            Console.WriteLine(db.Insert());
            Console.ReadKey();

5、定义一个控制台应用程序来调用:

三种注入方式:构造函数注入、属性注入、方法注入。

5.1 定义IHeadphone IMicrophone IPower 接口,代码如下:

    public interface IHeadphone
    {
    }
    public interface IMicrophone
    {
    }
    public interface IPower
    {
    }

 5.2 定义IPhone接口,代码如下:

    public interface IPhone
    {
        void call();
        IMicrophone iMicrophone { get; set; }
        IHeadphone iHeadphone { get; set; }
        IPower iPower { get; set; }
    }

5.3 IPhone接口的实现如下:

  public class ApplePhone : IPhone
    {
        [Dependency]//属性注入
        public IMicrophone iMicrophone { get ; set; }
        public IHeadphone iHeadphone { get ; set ; }
        public IPower iPower { get; set ; }

        [InjectionConstructor]//构造函数注入
        public ApplePhone(IHeadphone headphone)
        {
            this.iHeadphone = headphone;
            Console.WriteLine("{0}带参数构造函数",this.GetType().Name);
        }
        public void call()
        {
              Console.WriteLine("{0}打电话", this.GetType().Name);
        }
        [InjectionMethod]//方法注入
        public void Init2(IPower power)
        {
            this.iPower = power;
        }
    }

5.4 分别实现上面定义的接口

public class Headphone:IHeadphone
    {
        public Headphone()
        {
            Console.WriteLine("Headphone 被构造");
        }
    }
public class Microphone:IMicrophone
    {
        public Microphone()
        {
            Console.WriteLine("Microphone 被构造");
        }
    }
public class Power:IPower
    {
        public Power()
        {
            Console.WriteLine("Power 被构造");
        }
    }

从输出结果中可以看出三种注入方式的执行顺序:先执行构造函数注入,在执行属性注入,最后执行方法注入。

注意:默认情况下如果构造函数上面没有使用特性,那么默认找参数最多的构造函数执行注入。

 

IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType<IDbInterface, DbMSSQL>();//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType<IDbInterface, DbOracle>();//表示遇到IDbInterface的类型,创建DbMSSQL的实例
var db = container.Resolve<IDbInterface>();
Console.WriteLine(db.Insert());

从运行结果中可以看出,后面注册的类型会把前面注册的类型给覆盖掉,那么该如何解决呢?可以通过参数的方式来解决,代码如下:

IUnityContainer container = new UnityContainer();//1、定义一个空容器
container.RegisterType<IDbInterface, DbMSSQL>("sql");//2、注册类型,表示遇到IDbInterface的类型,创建DbMSSQL的实例
container.RegisterType<IDbInterface, DbOracle>("oracle");//表示遇到IDbInterface的类型,创建DbMSSQL的实例
var sql = container.Resolve<IDbInterface>("sql");
var oracle = container.Resolve<IDbInterface>("oracle");
Console.WriteLine(sql.Insert());
Console.WriteLine(oracle.Insert());

6、生命周期

IUnityContainer container = new UnityContainer();
container.RegisterType<IDbInterface, DbMSSQL>();
IDbInterface db1 = container.Resolve<IDbInterface>();
IDbInterface db2 = container.Resolve<IDbInterface>();
Console.WriteLine("HashCode:"+db1.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db2.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db1,db2));

表明db1和db2是两个不同的实例,即默认情况下生命周期是瞬时的,每次都是创建一个新的实例。

container.RegisterType<IDbInterface, DbMSSQL>(new TransientLifetimeManager());表示是瞬时生命周期,默认情况下即这种。

IUnityContainer container = new UnityContainer();
container.RegisterType<IDbInterface, DbMSSQL>(new ContainerControlledLifetimeManager());
IDbInterface db1 = container.Resolve<IDbInterface>();
IDbInterface db2 = container.Resolve<IDbInterface>();
Console.WriteLine("HashCode:" + db1.GetHashCode().ToString());
Console.WriteLine("HashCode:" + db2.GetHashCode().ToString());
Console.WriteLine(object.ReferenceEquals(db1, db2));

db1和db2是同一个实例。

container.RegisterType<IDbInterface, DbMSSQL>(new ContainerControlledLifetimeManager())表示是容器单例,每次都是同一个实例。

7、使用配置文件实现

在上面的例子中,所有的例子都是一直在依赖于细节,那么怎么解决不依赖于细节呢?答案是只能使用配置文件,配置文件如下:

<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
  </configSections>
  <unity>
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
    <containers>
      <container name="testContainer">
        <!--逗号前面是接口类型的完全限定名:命名空间+接口名称,逗号后面是DLL文件的名称 name解决同一个接口不同实例问题-->
        <register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.MSSQL.DbMSSQL, DataBase.MSSQL" name="sql"/>
        <register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.Oracle.DbOracle, DataBase.Oracle" name="oracle"/>
      </container>
    </containers>
  </unity>
</configuration>

注意:这个一个单独的配置文件,要把属性里面的复制到输出目录改为始终复制,那么这个配置文件才会生成到Debug目录里面。

引用里面只要对接口的引用了,没有对具体实现的引用。去掉了对细节的依赖。

注意:使用配置文件实现时,必须把接口的具体实现类复制到程序目录下面。

如果有额外添加了一种数据库,那么只需要修改配置文件,把新的实现类复制到程序目录下面即可实现程序的升级。

使用配置文件的时候,需要把UnityContainer容器定义为静态的,这样只需要读取一次配置文件即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值