Asp.net MVC企业级开发(01)---Autofac
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 的使用
- 获取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; public string 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 自动实例化。