1.1 控制反转

在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统的业务逻辑。同时,对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。但是,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson 1996年提出了IOC理论,用来实现对象之间的“解耦”.

我们来看下面的示例。

示例1

public class EmailService

{

public  void SendMessage()

{

Console.WriteLine("通过电子邮件发送消息");

}

}

public class MessageManager

{

private EmailService msgService;

public MessageManager()

{

msgService=new EmailService();

}

public void SendMsg()

{

msgService.SendMessage();

}

}

在示例1中,MessageManager 类依赖于EmailService类,两者之间存在耦合。 MessageManager类在构造函数内直接创建EmailService类的一个实例,换言之,MessageManager类精确的知道创建和使用了哪种类型的服务。这种耦合表示了代码的内部链接性。一个类知道与其交互的类的大量信息,我们称为高耦合。这就会增加软件修改的负担,因为修改一个类很可能破坏依赖于它的另一个类。

上面的代码设计还有一个问题:当MessageManager不想采用Email方式发送消息,而是采用其它方式,如微信,那么必须重新实现MessageManager类。

为了降低组件之间的耦合程度,一般采取两个独立但相关的步骤:

1在两块代码之间引入抽象层。

通常使用接口来代表两个类之间的抽象层。如实例2所示。

public interface IMessageService

{

void SendMessage();

}

public class EmailService : IMessageService

{

public  void SendMessage()

{

Console.WriteLine("通过电子邮件发送消息");

}

}

public class MessageManager

{

private IMessageService msgService;

public MessageManager()

{

msgService=new EmailService();

}

public void SendMsg()

{

msgService.SendMessage();

}

}

2把选择抽象实现的责任移到消费者的外部。

需要把EmailService类的创建移到MessageManager类的外面。

把依赖的创建移到使用这些依赖类的外部,这称为控制反转模式,之所以这样命名,是因为反转的是依赖的创建,正因为如此,才消除了消费者类对依赖创建的控制。

控制反转(Inversion of Control,即IOC)模式是抽象的:它只是表述应该从消费者类中移除依赖创建,而没有表述如何实现。

1.2 依赖注入

依赖注入(Dependence Injection,即DI)是一种控制反转的形式。其意思是自身对象中的内置对象是通过注入的方式进行创建。注入方式通常采用构造函数注入或属性注入。

1.2.1 构造函数注入

示例3

public class MessageManager

{

private IMessageService msgService;

public MessageManager(IMessaageService service)

{

msgService=service;

}

public void SendMsg()

{

msgService.SendMessage();

}

}

示例3有一个显著的优点,它极大地简化了构造函数的实现。组件总是期望创建它的类能够传递需要的依赖。而它只需要存储IMessaageService接口的实例以便之后使用,不需要知道它自己的依赖项。另一个有点就是需求的透明性。任何要创建MessageManager类实例的代码都能查看构造函数,并精确的知道那些内容是使用该类必须的。

1.2.2 属性注入

顾名思义,该方式是通过设置对象上的公共属性而不是通过使用构造函数参数来注入依赖的。如示例4所示。

示例4

public class MessageManager

{

public IMessageService msgService {get; set;}

public void SendMsg()

{

if (msgService ==null)

{

Throw new InvalidOperationException();

}

msgService.SendMessage();

}

}

上面的两种注入方式,都需要手动提供所需的依赖项,也就意味着都需要我们知道如何来满足每一部分的需要。

依赖注入容器是一个使依赖解析变得简单的一种方式。

1.3 依赖注入框架---Autofac

Autofac是一款IOC容器框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等框架,它很轻量级,性能上也很高。

1.3.1 Autofac 的使用

  1. 获取Autofac

通过VS中的NuGet来加载AutoFac,引入成功后引用就会出现Autofac。如图1所示。

图1-1 获取Autofac

示例5中,数据层有两个类,一个是Oracle 一个是SQLSERVER。我们在使用的时候可以选择调用那个数据库。通过Autofac来完成构造函数注入。

示例5

/// <summary>

/// 数据源操作接口

/// </summary>

public interface IDataSource

{

/// <summary>

/// 获取数据

/// </summary>

/// <returns></returns>

string GetData();

}

/// <summary>

/// SQLSERVER数据库

/// </summary>

public class Sqlserver : IDataSource

{

public string GetData()

{

return "通过SQLSERVER获取数据";

}

}

/// <summary>

/// ORACLE数据库

/// </summary>

public class Oracle : IDataSource

{

public string GetData()

{

return "通过Oracle获取数据";

}

}

/// <summary>

/// 数据源管理类

/// </summary

public class DataSourceManager
  {

IDataSource _ds;
        /// <summary>
        /// 根据传入的类型动态创建对象
        /// </summary>
        /// <param
name="ds"></param>
        public
DataSourceManager(IDataSource ds)
        {
            _ds = ds;
        }

public string GetData()
        {
            return _ds.GetData();
        }
    }

static void Main(string[] args)

{

//获取Autofac容器构造器

var builder = new ContainerBuilder();

// 注册类型

builder.RegisterType<DataSourceManager>();

// 注册实例(即将SqlServer类型注册为IDataSource的实例

builder.RegisterType<Sqlserver>().As<IDataSource>();

// 由构造器创建Ioc容器

using (var
container = builder.Build())

{

// 解析实例

var manager =
container.Resolve<DataSourceManager>();

Console.WriteLine(manager.GetData());

Console.ReadLine();

}

}

示例5所示代码展示了Autofac的基本使用,在实际开发中,还有很多其它的用法。

1 AsImplementedInterfaces (注册多个接口)

在很多情况下,一个类可能实现了多个接口,如果按照实例5的写法,我们需要为每一个接口都注册实例,非常繁琐。这时候就可以使用AsImplementedInterfaces() 方法,可以把一个类注册给它实现的全部接口。代码如下:

builder.RegisterType<Sqlserver>().AsImplementedInterfaces()

  2利用反射注册

在实际开发中,往往会有很多接口需要注册,这时我们可以通过反射的方式将其全部注册。代码如下:

//加载实现类的程序集

Assembly asm =
Assembly.Load("DAL.BLL");

builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();

我们可以在配置文件中将程序集信息在<appSettings>节点中进行配置,来减少对程序集的引用,彻底解除耦合。

3 PropertiesAutowired (利用属性注入)

上述通过Autofac进行注入都是针对构造函数进行的注册,如果使用属性进行注册需要使用PropertiesAutowired()方法。

//加载实现类的程序集

Assembly asm =
Assembly.Load("DAL.BLL");

builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();

4)一个接口多个实现

一个接口有多个实现类的情况,如实例5中,SqlServer 和 Oracle 都实现了IDataSource接口,若有同时注册,需要使用Resolve<IEnumerable<IAnimalBLL>>()即可。如实例6所示。

实例6

Assembly asm = Assembly.Load("DAL.BLL");

builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();

using (var container =
builder.Build())

{

IEnumerable<IDataSource> dals =

container.Resolve<IEnumerable<IDataSource>>();

foreach(var
dal in dals)

{

Console.WriteLine(dal.GetData());

}

Console.ReadLine();

}

也可以通过Named()方法在指定要想获取的类型。如示例7所示。

示例7

//获取Autofac容器构造器

var builder = new ContainerBuilder();

// 注册类型

builder.RegisterType<DataSourceManager>();

// 注册实例

builder.RegisterType<Sqlserver>().Named<IDataSource>("SqlServer");

     
builder.RegisterType<Sqlserver>().
Named<IDataSource>("Oracle");

// 由构造器创建Ioc容器

using
(var container = builder.Build())

{

// 解析实例

var manager =
container.ResolveNamed<DataSourceManager>("SqlServer");

Console.WriteLine(manager.GetData());

Console.ReadLine();

}

1.3.2
通过配置方式使用Autofac

通过配置实现Autofac 需要添加对Autofac.Configuration.dll的引用。

示例8

配置文件

<configuration>

<configSections>

<section name="autofac"
type="Autofac.Configuration.SectionHandler,Autofac.Configuration"></section>

</configSections>

<autofac defaultAssembly="AutoFacDemo">

<components>

<component 
type="AutoFacDemo.Model.Oracle,AutoFacDemo"
service="AutoFacDemo.Model.IDataSource" />

</components>

</autofac>

<startup>

<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5" />

</startup>

</configuration>

C#代码

var builder = new
ContainerBuilder();

builder.RegisterType<DataSourceManager>();

builder.RegisterModule(

new
ConfigurationSettingsReader("autofac" ));

1.4 在Asp.net
Mvc中使用Autofac

在.net Mvc中使用Autofac,原理和上述讲解一样,区别在于需要通过Nuget多安装一个程序集 Autofac.Mvc5

步骤1

在MVC项目中添加对 Autofac 和 Autofac.Mvc5 的引用。

步骤2

创建配置类。在此我们通过属性注入的方式进行注入,如示例9所示。

示例9

public class AutofacConfig

{

public static void Register()

{

var builder = new ContainerBuilder();

//加载当前程序集

Assembly controllerAss =
Assembly.GetExecutingAssembly();

//Assembly controllerAss =
Assembly.Load("OA.Web");

//注册所有的Controller

builder.RegisterControllers(controllerAss).PropertiesAutowired();

//注册数据访问层程序集

Assembly resAss =
Assembly.Load("OA.DAL");

builder.RegisterTypes(resAss.GetTypes()).AsImplementedInterfaces();

//注册业务层程序集

Assembly bllAss =
Assembly.Load("OA.BLL");

builder.RegisterTypes(bllAss.GetTypes()).AsImplementedInterfaces();

var container = builder.Build();

//当mvc创建controller对象的时候,都是由AutoFac为我们创建

//Controller对象

DependencyResolver.SetResolver(new

AutofacDependencyResolver(container));

}

}

示例9中对数据层和业务层的注入请根据实际情况进行注册,不是必须的。

步骤3

在Global.asax 的Application_Start()
注册自己的控制器类

protected void
Application_Start()

{

AreaRegistration.RegisterAllAreas();

RouteConfig.RegisterRoutes(RouteTable.Routes);

//Autofac配置

AutofacConfig.Register();

}

至此,在MVC中使用 Autofac 的配置步骤完成。

在Controller中的使用如下:

public
class HomeController :Controller

{

public
IDataSource dataSource { get ; set; }

public  ActionResult Index( )

{

var data =
dataSource.GetData( );

return
View(data);

}

}

dataSource 
属性会被 Autofac 自动实例化。

Asp.net MVC企业级开发(01)---Autofac的更多相关文章

  1. Asp.net MVC企业级开发(04)---SignalR消息推送

    Asp.net SignalR是微软为实现实时通信而开发的一个类库.可以适用于以下场景: 聊天室,如在线客服系统,IM系统等 股票价格实时更新 消息的推送服务 游戏中人物位置的实时推送 SignalR ...

  2. Asp.net MVC企业级开发(09)---T4模板

    T4即为Text Template Transformation Toolkit,一种可以由自己去自定义规则的代码生成器.根据业务模型可生成任何形式的文本文件或供程序调用的字符串 在VS中T4模板是没 ...

  3. Asp.net MVC企业级开发(02)---Log4net

    Log4Net 是用来记录日志的,可以将程序运行过程中的信息输出到一些地方(文件.数据库.EventLog等).日志就是程序的“黑匣子”,可以通过日志查看系统的运行过程,从而发现系统的问题. 日志的作 ...

  4. Asp.net Mvc模块化开发之分区扩展框架

    对于一个企业级项目开发,模块化是非常重要的. 默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团 ...

  5. ASP.NET MVC企业级项目框架

    ASP.NET MVC企业级项目框架 MVC项目搭建笔记---- 项目框架采用ASP.NET MVC+Entity Framwork+Spring.Net等技术搭建,搭建过程内容比较多,结合了抽象工厂 ...

  6. Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门

    Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门 第一节: 前言(技术简介) EasyUI 是一套 js的前端框架 利用它可以快速的开发出好看的 前端系统 web ...

  7. 关于《ASP.NET MVC企业级实战》

    大家好,我的书<ASP.NET MVC企业级实战>已经出版啦,感谢大家过去的关注与支持!前言部分,出版的时候漏了部分内容,我这里将其贴出来. 本书提供源码和教学PPT课件!(源码在书中第3 ...

  8. Asp.net Mvc模块化开发系列(目录)

    模块化开发是非常重要的,模块化开发是个系统性问题,为此我觉得有必须要写一个系列的文章才能基本说的清楚 那又为什么要写一个目录呢? 其一.是对我昨天承诺写一个系列新的文章的回应 其二.是先写出一个大纲, ...

  9. ASP.NET MVC 网站开发总结(三) ——图片截图上传

    本着简洁直接,我们就直奔主题吧,这里需要使用到一个网页在线截图插件imgareaselect(请自行下载). 前台页面: <!DOCTYPE html> <html> < ...

随机推荐

  1. Jenkins 打tag回滚

    利用jenkins,从gitlab上拉取代码,然后发布,如果想进行代码回退,其实还是代码发布,拉取的时候,选择合适的标签. 一.利用Git parameter插件选择branch或tag.下面的文本参 ...

  2. vnc服务器和windows2012密钥

    [root@localhost ~]# vncserver #启动服务器 windows 2012 64位-server版本的密钥 Windows Server 2012 Standard 密钥:NB ...

  3. [PHP] substr占用内存谨慎使用

    在下面的场景中使用substr的时候, 有时候会报超出内存fatal error ,当curl读取的内容过大的时候 $header_size = curl_getinfo($curl_handle, ...

  4. 安装教程-VMware 12 安装Ubuntu 19.04 桌面版

    VMware 12 安装Ubuntu 19.04 桌面版 1.实验描述 在虚拟机中,手动安装 Ubuntu 19.04 操作系统,为学习 Linux 桌面版提供平台,因此,有的参数有些差异,请勿较真. ...

  5. 其他综合-CentOS 7 rsync+nginx实现公网yum源

    CentOS 7 rsync+nginx实现公网yum源 1.实验描述 镜像同步公网 yum 源上游 yum 源必须要支持 rsync 协议,否则不能使用 rsync 进行同步.CentOS 7.6 ...

  6. Pyqt5开发一款小工具(翻译小助手)

    翻译小助手 开发需求 首先五月份的时候,正在学习爬虫的中级阶段,这时候肯定要接触到js逆向工程,于是上网找了一个项目来练练手,这时碰巧有如何进行对百度翻译的API破解思路,仿造网上的思路,我摸索着完成 ...

  7. windows API下的模板缓冲(stencil buffer)

    在windows API搭建的OpenGL窗口中使用模板缓冲,需要在像素格式描述表中设置stencil buffer位宽为8,这样窗口会自动生成stencil buffer,然后可以在opengl环境 ...

  8. 使用spring aop 记录接口日志

    spring配置文件中增加启用aop的配置 <!-- 增加aop 自动代理配置 --> <aop:aspectj-autoproxy /> 切面类配置 package com. ...

  9. 2.2 Scala基础知识

    一.基本数据类型和变量 1.基本数据类型 java中每一个数据类型都是一个类: scala没有自己定义String类型,String类型是从java.lang.String照搬的. 字面量(liter ...

  10. reduce计算数组中每个元素出现的次数 数组去重的几种方式 将多维数组转化为一维

    // js计算数组中每个元素出现的次数 // var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']; // var countedNames = ...