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. Win2003下安装PHP5.2.0+MySql5.0.27+PHPMyAdmin2.9.1的配置方法

    先下载所需要安装的东东~~ PHP 5.2.0 官方下载地址:http://www.php.net/downloads.php mysql-5.0.27 官方下载地址:http://dev.mysql ...

  2. Ubuntu启动器快捷方式文件解析

    快捷方式名称 app_name.desktop 路径: /usr/share/applications/app_name.desktop # 简洁快捷方式格式 [Desktop Entry] Name ...

  3. mysql中的case when then 的用法

    将立立饭饭的性别变更为女 ,烦烦 嗯嗯 问我的性别变更为男 update `table` set sex = ( case when sname in('立立','饭饭') then '女' else ...

  4. 两道DP,四年修一次路

    第十一届:山区修路 题目描述 SNJ位于HB省西部一片群峰耸立的高大山地,横亘于A江.B水之间,方圆数千平方公里,相传上古的神医在此搭架上山采药而得名.景区山峰均在海拔3000米以上,堪称" ...

  5. jenkins部署报404错误

    环境:tomcat 7+jdk1.7+win10 64 jenkins_1.5.23 部署完成后服务器启动输入网址:http://192.168.3.100:8080/jenkins打开无法访问报40 ...

  6. 【使用篇二】SpringBoot异常处理(9)

    异常的处理方式有多种: 自定义错误页面 @ExceptionHandler注解 @ControllerAdvice+@ExceptionHandler注解 配置SimpleMappingExcepti ...

  7. JDOJ 1775: 求N!中0的个数

    JDOJ 1775: 求N!中0的个数 JDOJ传送门 Description 求N!结果中末尾0的个数 N! = 1 * 2 * 3 ....... N Input 输入一行,N(0 < N ...

  8. USACO wormhole

    洛谷 P1444 [USACO1.3]虫洞wormhole https://www.luogu.org/problemnew/show/P1444 JDOJ 2386: USACO 2013 Dec ...

  9. c++的CreateFile导致内存不能为written错误

    LPCWSTR szFileName; szFileName=argv[2]; //LPCWSTR szFileName=L"test.txt";//文件名字可以根据自己的需要修改 ...

  10. 递归函数详解——VS调试教你理解透彻递归

    #include <stdio.h> #include <stdlib.h> int recursion(int); ; int main(void) { recursion( ...