一步一步学EF系列【6、IOC 之AutoFac】
2015-09-14 08:27
211 查看
前言
之前的前5篇作为EF方面的基础篇,后面我们将使用MVC+EF并且使用IOC,Repository,UnitOfWork,DbContext来整体来学习。因为后面要用到IOC,所以本篇先单独先学习一下IOC,我们本本文单独主要学习Autofac,其实对于Autofac我也是边学边记录。不对的地方,也希望大家多多指导。个人在学习过程中参考博客:
AutoFac文档:
AutoFac使用方法总结:PartI:
为什么使用AutoFac?
Autofac是.NET领域最为流行的IOC框架之一,传说是速度最快的一个:上面的优点我也是拷的别人文章里面的,上面的这个几乎所有讲Autofac博文都会出现的。这个也是首次学习,所以我们还是记录的细一点。
怎么使用Autofac
1.1我们先定义一个数据访问的接口和访问类。
///<summary> ///数据源操作接口 ///</summary> publicinterfaceIDataSource { ///<summary> ///获取数据 ///</summary> ///<returns></returns> stringGetData(); }
///<summary> ///SQLSERVER数据库 ///</summary> classSqlserver:IDataSource { publicstringGetData() { return"通过SQLSERVER获取数据"; } }
///<summary> ///ORACLE数据库 ///</summary> publicclassOracle:IDataSource { publicstringGetData() { return"通过Oracle获取数据"; } }
最普通的方式大家都会的吧!如果最普通的方式调用SQLSERVER怎么写?
staticvoidMain(string[]args) { IDataSourceds=newSqlserver(); Console.WriteLine(ds.GetData()); Console.ReadLine(); }
调用Oracle的话newOracle()就可以了。如果这个都不能理解的话,那学习这个你就很费劲了。
改进一下代码。我们在加入一个DataSourceManager类来看一下
///<summary>
///数据源管理类
///</summary
publicclassDataSourceManager { IDataSource_ds; ///<summary> ///根据传入的类型动态创建对象 ///</summary> ///<paramname="ds"></param> publicDataSourceManager(IDataSourceds) { _ds=ds; } publicstringGetData() { return_ds.GetData(); } }
这样写的好处是什么,这样加入加入新的数据源,只用调用的时候传入这个对象就可以,就会自动创建一个对应的对象。那接下如果要调用SQLSERVER怎么写。看代码
DataSourceManagerdsm=newDataSourceManager(newSqlserver());
Console.WriteLine(dsm.GetData()); Console.ReadLine();
1.2注入实现构造函数注入
上面的DataSourceManager的动态创建的方式就是因为又有个带IDataSource的参数的构造函数,只要调用者传入实现该接口的对象,就实现了对象创建。那我们看看怎么使用AutoFac注入实现构造函数注入
varbuilder=newContainerBuilder(); builder.RegisterType<DataSourceManager>(); builder.RegisterType<Sqlserver>().As<IDataSource>(); using(varcontainer=builder.Build()) { varmanager=container.Resolve<DataSourceManager>(); Console.WriteLine(manager.GetData()); Console.ReadLine(); }
上面的就是AutoFac构造函数注入,他给IDataSource注入的是Sqlserver所以我们调用的数据,返回的就是Sqlserver数据。那下面我们具体的了解一下AutoFac的一些方法
1.3Autofac方法说明
(1)builder.RegisterType<Object>().As<Iobject>():注册类型及其实例。例如上面就是注册接口IDataSource的实例Sqlserver
(2)IContainer.Resolve<IDAL>():解析某个接口的实例。例如一下代码,我可以解析接口返回的就是Sqlserver实例
varbuilder=newContainerBuilder();
//builder.RegisterType<DataSourceManager>();
builder.RegisterType<Sqlserver>().As<IDataSource>();
using(varcontainer=builder.Build())
{
varmanager=container.Resolve<IDataSource>();
Console.WriteLine(manager.GetData());
Console.ReadLine();
}
(3)builder.RegisterType<Object>().Named<Iobject>(stringname):为一个接口注册不同的实例。有时候难免会碰到多个类映射同一个接口,比如Sqlerver和Oracle都实现了IDalSource接口,为了准确获取想要的类型,就必须在注册时起名字。
varbuilder=newContainerBuilder(); builder.RegisterType<Sqlserver>().Named<IDataSource>("SqlServer"); builder.RegisterType<Oracle>().Named<IDataSource>("Oracel"); using(varcontainer=builder.Build()) { varmanager=container.ResolveNamed<IDataSource>("Oracel"); Console.WriteLine(manager.GetData()); Console.ReadLine(); }
运行后的代码。
(4)IContainer.ResolveNamed<IDAL>(stringname):解析某个接口的“命名实例”。例如上面的实例最后一行代码container.ResolveNamed<IDataSource>("Oracel");就是解析IDataSource的命名实例Oracel。
(5)builder.RegisterType<Object>().Keyed<Iobject>(Enumenum):以枚举的方式为一个接口注册不同的实例。有时候我们会将某一个接口的不同实现用枚举来区分,而不是字符串。
这个方法是完全可以替代builder.RegisterType<Object>().Named<Iobject>(stringname),这个列子就不演示了吧!和上面的一个意思。
(6)IContainer.ResolveKeyed<IDAL>(Enumenum):根据枚举值解析某个接口的特定实例。这个和上面的都一样也就不演示了。
(7)builder.RegisterType<Worker>().InstancePerDependency():用于控制对象的生命周期,每次加载实例时都是新建一个实例,默认就是这种方式。调用的话
builder.RegisterType<Sqlserver>().Keyed<IDataSource>("Sqlserver").InstancePerDependency();
(8)builder.RegisterType<Worker>().SingleInstance():用于控制对象的生命周期,每次加载实例时都是返回同一个实例
(9)IContainer.Resolve<T>(NamedParameternamedParameter):在解析实例T时给其赋值,这个就是给你定义的方法的参数传值。
IDataSource_ds;我把DataSourceManager的构造方法加了个name参数,然后我调用的时候:
stringName;
///<summary>
///根据传入的类型动态创建对象
///</summary>
///<paramname="ds"></param>
publicDataSourceManager(stringname,IDataSourceds)
{
_ds=ds;
Name=name;
}
publicstringGetData()
{
returnName+":"+_ds.GetData();
}
varmanager=container.Resolve<DataSourceManager>(newNamedParameter("name","STONE刘先生"));
运行后的代码:
1.4通过配置的方式使用AutoFac
在演示一下怎么通过配置文件来配置注册。这块就简单讲,下面的是我的web.config。<configuration>
<configSections>
<sectionname="autofac"type="Autofac.Configuration.SectionHandler,Autofac.Configuration"></section>
</configSections>
<autofacdefaultAssembly="AutoFacDemo">
<components>
<componenttype="AutoFacDemo.Model.Oracle,AutoFacDemo"service="AutoFacDemo.Model.IDataSource"/>
</components>
</autofac>
<startup>
<supportedRuntimeversion="v4.0"sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
后台的调用代码
varbuilder=newContainerBuilder();
builder.RegisterType<DataSourceManager>();
builder.RegisterModule(newConfigurationSettingsReader("autofac"));
using(varcontainer=builder.Build())
{
varmanager=container.Resolve<DataSourceManager>(newNamedParameter("name","STONE刘先生"));
Console.WriteLine(manager.GetData());
Console.ReadLine();
}
这里需要注意的是需要引用Autofac.Configuration.dll,否则没有办法使用ConfigurationSettingsReader。
还有一个需要注意的就是你的配置文件要命名空间,类名要写对。
动手尝试一下吧!
MVC下面使用Autofac
引用和上面的控制台程序的原理是一模一样的。但是区别就在于要多添加一个引用
案例还是用上面的案例。我是把之前的接口和类拷贝到MVC项目里面作为下面演示。代码就不在写出来了,一模一样的。
1、首先在函数Application_Start()注册自己的控制器类
MVC下怎么配置可以直接看如下代码,我把注释写的也很详细。
protectedvoidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
//创建autofac管理注册类的容器实例
varbuilder=newContainerBuilder();
//下面就需要为这个容器注册它可以管理的类型
//builder的Register方法可以通过多种方式注册类型,之前在控制台程序里面也演示了好几种方式了。
builder.RegisterType<Sqlserver>().As<IDataSource>();
//builder.RegisterType<DefaultController>().InstancePerDependency();
//使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册 builder.RegisterControllers(Assembly.GetExecutingAssembly());
//生成具体的实例
varcontainer=builder.Build();
//下面就是使用MVC的扩展更改了MVC中的注入方式.
DependencyResolver.SetResolver(newAutofacDependencyResolver(container));
}
需要解释的是:
1、大家看下面的这句,这句的作用就是再MVC下面你必须要注册一下Controller,否则没有办法注入。
//使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册 builder.RegisterControllers(Assembly.GetExecutingAssembly());
我们通过使用RegisterControllers就可以解决。那如果不用RegisterControllers我就想一个个注册的话怎么弄?学技术有时候不要只管会用有的时候你也要理解人家提供的方法背后是怎么做的。看到这里你知道怎么做么?先考虑1分钟,不要记得往下看。答案其实在上面讲控制台程序使用Autofac的时候已经讲过了。好吧,我来详细讲一下,我先把之前控制台程序的代码贴出来。
publicclassDataSourceManager
{
IDataSource_ds;
stringName;
///<summary>
///根据传入的类型动态创建对象
///</summary>
///<paramname="ds"></param>
publicDataSourceManager(stringname,IDataSourceds)
{
_ds=ds;
Name=name;
}
publicstringGetData()
{
returnName+":"+_ds.GetData();
}
}
这个类还记得吗?不记得在看之前写的文章。这个类有个IDataSource作为参数的构造方法。然后我们在看一下使用时候的代码?
varbuilder=newContainerBuilder();
builder.RegisterType<DataSourceManager>();
builder.RegisterType<Sqlserver>().As<IDataSource>();
using(varcontainer=builder.Build())
{
varmanager=container.Resolve<DataSourceManager>(newNamedParameter("name","STONE刘先生"));
Console.WriteLine(manager.GetData());
Console.ReadLine();
}
看到了吗?container.Resolve<DataSourceManager>()这里通过Resolve解析DataSourceManager实例,对于DataSourceManager类型,我们为Autofac提供了类型,但是当Autofac创建DataSourceManager的实例,调用它的构造函数的时候,它的构造函数需要提供一个IDataSource的实例作为参数的,Autofac会在自己的容器里,找注册过IDataSource的实例,并且通过AsImplementedInterfaces()方法,指明为接口IDataSource提供的实例。然后作为创建DataSourceManager时,提供给构造函数的参数。这整个原理不知道这样讲你能听懂吗?
大家上面提出的如果不用RegisterControllers来,需要手动添加怎么做?答案就是要写若干个这个方法。
builder.RegisterType<DefaultController>().InstancePerDependency();
注:DefaultController控制器的名称,你可要试着把RegisterControllers删除掉,用上面的这句来尝试一下。但是实际的项目中最好是用RegisterControllers。
2、如果没有写builder.RegisterControllers<>,而且控制器也没有通过builder.RegisterType<>注册,你会看到如下的错误
整个MVC使用autofac配置的工作就完成了。那接下来直接来看代码里面怎么使用。
2、添加控制器,并注入依赖代码
publicclassDefaultController:Controller
{
IDataSourceds;
//接口定义构造函数注入
publicDefaultController(IDataSource_ds)
{
ds=_ds;
}
//GET:Default
publicActionResultIndex()
{
//调用具体类的具体方法返回结果赋值给ViewBag.Message
ViewBag.Message="STONE刘先生:"+ds.GetData();
returnView();
}
}
整个功能请求的数据添加到ViewBag然后在页面上面显示出来,也比较简单的。
运行后的效果:
成功了!
补充一下:
上面的列子演示的是构造函数注入,那看看能否改成属性注入。
看如下代码,IDataSource加上get;set就变成属性了:
publicclassDefaultController:Controller
{
publicIDataSourceds{get;set;}
//接口定义构造函数注入
//publicDefaultController(IDataSource_ds)
//{
//ds=_ds;
//}
//GET:Default
publicActionResultIndex()
{
//调用具体类的具体方法返回结果赋值给ViewBag.Message
ViewBag.Message="STONE刘先生:"+ds.GetData();
returnView();
}
}
如果现在任何地方都不改的情况下,你看看会报什么错,是不是提醒ds为null,那怎么支持属性注入呢!我看了好久
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
把Global.asax里面的这句改成如上这句就好了!