AutoFac是.Net平台下的IOC容器产品,传说是速度最快的一个。
优点:
- 它是C#语言联系很紧密,也就是说C#里的很多编程方式都可以为Autofac使用,例如可以用Lambda表达式注册组件
- 较低的学习曲线,学习它非常的简单,只要你理解了IoC和DI的概念以及在何时需要使用它们
- XML配置支持
- 自动装配
- 与ASP.Net MVC集成
- 微软的Orchad开源程序使用的就是Autofac,从该源码可以看出它的方便和强大
怎么使用Autofac
通过VS中的NuGet来加载AutoFac,引入成功后引用就会出现Autofac。(最后添加了AutoFac.dll即为成功)
先定义一个数据访问的接口和访问类。
/// <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获取数据";
}
}
如果最普通的方式调用SQLSERVER怎么写?
static void Main(string[] args)
{
IDataSource ds = new Sqlserver();
Console.WriteLine(ds.GetData());
Console.ReadLine();
}
调用Oracle的话new Oracle()就可以了。
改进一下代码。我们在加入一个DataSourceManager类来看一下
public class DataSourceManager
{
IDataSource _ds;
/// <summary>
/// 根据传入的类型动态创建对象
/// </summary>
/// <param name="ds"></param>
public DataSourceManager(IDataSource ds)
{
_ds = ds;
}
public string GetData()
{
return _ds.GetData();
}
}
这样写的好处是什么,这样加入加入新的数据源,只用调用的时候传入这个对象就可以,就会自动创建一个对应的对象。那接下如果要调用SQLSERVER怎么写。看代码
DataSourceManager dsm = new DataSourceManager(new Sqlserver());
Console.WriteLine(dsm.GetData());
Console.ReadLine();
上面的DataSourceManager的动态创建的方式就是因为又有个带IDataSource的参数的构造函数,只要调用者传入实现该接口的对象,就实现了对象创建。
那我们看看怎么使用AutoFac注入实现构造函数注入
builder.RegisterType<DataSourceManager>();
builder.RegisterType<Sqlserver>().As<IDataSource>();
using (var container = builder.Build())
{
var manager = container.Resolve<DataSourceManager>();
Console.WriteLine(manager.GetData());
Console.ReadLine();
}
上面的就是AutoFac构造函数注入,他给IDataSource注入的是Sqlserver,就是我们调用的数据,返回的就是Sqlserver数据。那下面我们具体的了解一下AutoFac的一些方法
Autofac方法说明
builder.RegisterType().As():注册类型及其实例。例如上面就是注册接口IDataSource的实例Sqlserver
IContainer.Resolve():解析某个接口的实例。例如下面代码,我可以解析接口返回的就是Sqlserver实例
var builder = new ContainerBuilder();
//builder.RegisterType();
builder.RegisterType().As();
using (var container = builder.Build())
{
var manager = container.Resolve();
Console.WriteLine(manager.GetData());
Console.ReadLine();
}- builder.RegisterType().Named(string name):为一个接口注册不同的实例。有时候难免会碰到多个类映射同一个接口,比如Sqlerver和Oracle都实现了IDalSource接口,为了准确获取想要的类型,就必须在注册时起名字。
IContainer.ResolveNamed(string name):解析某个接口的“命名实例”。例如上面的实例最后一行代码
builder.RegisterType<Oracle>().Named<IDataSource>("OracelDB");
container.ResolveNamed<IDataSource>("OracelDB");
- imagebuilder.RegisterType().Keyed(Enum enum):以枚举的方式为一个接口注册不同的实例。有时候我们会将某一个接口的不同实现用枚举来区分,而不是字符串。
IContainer.ResolveKeyed<IDAL>(Enum enum):根据枚举值解析某个接口的特定实例。这个和上面的都一样 也就不演示了。
- builder.RegisterType().InstancePerDependency():用于控制对象的生命周期,每次加载实例时都是新建一个实例,默认就是这种方式。调用的话
builder.RegisterType<Sqlserver>().Keyed<IDataSource>("Sqlserver").InstancePerDependency();
builder.RegisterType().SingleInstance():用于控制对象的生命周期,每次加载实例时都是返回同一个实例
IContainer.Resolve(NamedParameter namedParameter):在解析实例T时给其赋值,这个就是给你定义的方法的参数传值。
IDataSource _ds;string Name;
/// <summary>
/// 根据传入的类型动态创建对象
/// </summary>
/// <param name="ds"></param>
public DataSourceManager(string name, IDataSource ds)
{
_ds = ds;
Name = name;
}
public string GetData()
{
returnName + ":" + _ds.GetData();
}
我把DataSourceManager的构造方法加了个name参数,然后我调用的时候:
var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE刘先生"));
MVC下面使用Autofac
引用和上面的控制台程序的原理是一模一样的。但是区别就在于要多添加一个引用
1、首先在函数Application_Start() 注册自己的控制器类
MVC下怎么配置可以直接看如下代码
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
//创建autofac管理注册类的容器实例
var builder = new ContainerBuilder();
//下面就需要为这个容器注册它可以管理的类型
//builder的Register方法可以通过多种方式注册类型,之前在控制台程序里面也演示了好几种方式了。
builder.RegisterType<Sqlserver>().As<IDataSource>();
//builder.RegisterType<DefaultController>().InstancePerDependency();
//使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//生成具体的实例
var container = builder.Build();
//下面就是使用MVC的扩展 更改了MVC中的注入方式.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
需要解释的是:
1、下面的这句的作用就是再MVC下面你必须要注册一下Controller,否则没有办法注入。
//使用Autofac提供的RegisterControllers扩展方法来对程序集中所有Controller一次性的完成注册
builder.RegisterControllers(Assembly.GetExecutingAssembly());
我们通过使用RegisterControllers就可以解决。那如果不用RegisterControllers ,我就想一个个注册的话怎么弄?答案其实在上面讲控制台程序使用Autofac的时候已经讲过了。先把之前控制台程序的代码贴出来
public class DataSourceManager
{
IDataSource _ds;
string Name;
/// <summary>
/// 根据传入的类型动态创建对象
/// </summary>
/// <param name="ds"></param>
public DataSourceManager(string name, IDataSource ds)
{
_ds = ds;
Name = name;
}
public string GetData()
{
return Name + ":" + _ds.GetData();
}
}
这个类有个IDataSource 作为参数的构造方法。
var builder = new ContainerBuilder();
builder.RegisterType<DataSourceManager>();
builder.RegisterType<Sqlserver>().As<IDataSource>();
using (var container = builder.Build())
{
var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE刘先生"));
Console.WriteLine(manager.GetData());
Console.ReadLine();
}
container.Resolve()这里通过Resolve解析DataSourceManager实例,对于DataSourceManager类型,我们为Autofac提供了类型, 但是当Autofac创建DataSourceManager的实例, 调用它的构造函数的时候,它的构造函数需要提供一个IDataSource的实例作为参数的,Autofac会在自己的容器里,找注册过IDataSource的实例,并且通过AsImplementedInterfaces()方法,指明为接口IDataSource提供的实例。然后作为创建DataSourceManager时,提供给构造函数的参数。
Q:如果不用RegisterControllers来,需要手动添加怎么做?
A:写若干个重复的方法。
builder.RegisterType<DefaultController>().InstancePerDependency();
注:DefaultController 控制器的名称,你可要试着把RegisterControllers删除掉,用上面的这句来尝试一下。但是实际的项目中最好是用RegisterControllers。
2、如果没有写builder.RegisterControllers<> ,而且控制器也没有通过builder.RegisterType<>注册, 你会看到如下的错误
整个MVC 使用autofac配置的工作就完成了。那接下来直接来看代码里面怎么使用。
2、添加控制器,并注入依赖代码
public class DefaultController : Controller
{
IDataSource ds;
// 接口定义 构造函数注入
public DefaultController(IDataSource _ds)
{
ds = _ds;
}
// GET: Default
public ActionResult Index()
{
//调用具体类的具体方法返回结果 赋值给ViewBag.Message
ViewBag.Message = "STONE刘先生:" + ds.GetData();
return View();
}
}
整个功能请求的数据添加到ViewBag然后在页面上面显示出来,也比较简单的。
运行后的效果:
成功了!
补充一下:
上面的列子演示的是构造函数注入,那看看能否改成属性注入。
看如下代码,IDataSource 加上get;set就变成属性了:
public class DefaultController : Controller
{
public IDataSource ds { get; set; }
// 接口定义 构造函数注入
//public DefaultController(IDataSource _ds)
//{
// ds = _ds;
//}
// GET: Default
public ActionResult Index()
{
//调用具体类的具体方法返回结果 赋值给ViewBag.Message
ViewBag.Message = "STONE刘先生:" + ds.GetData();
return View();
}
}
如果现在任何地方都不改的情况下,你看看会报什么错,是不是提醒ds为null,那怎么支持属性注入呢! 我看了好久
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
把Global.asax里面的改成如上这句 就好了!
补充,自动注入
Autofac提供一个RegisterAssemblyTypes方法。它会去扫描所有的dll并把每个类注册为它所实现的接口。既然能够自动注入,那么接口和类的定义一定要有一定的规律。我们可以定义IDependency接口的类型,其他任何的接口都需要继承这个接口。比如
public interface IDependency
{
}
/// <summary>
/// 业务逻辑实现
/// </summary>
public class PostService : IDependency
{
public IPostService postService { get; set; }
........
}
自动注入原理说明:
首先我们去找到所有Dll,再去找到实现了IDependency接口的类,然后使用RegisterAssemblyTypes进行注入。
Assembly[] assemblies = Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath, "*.dll").Select(Assembly.LoadFrom).ToArray();
//注册所有实现了 IDependency 接口的类型
Type baseType = typeof(IDependency);
builder.RegisterAssemblyTypes(assemblies)
.Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
.AsSelf().AsImplementedInterfaces()
.PropertiesAutowired().InstancePerLifetimeScope();
//注册MVC类型
builder.RegisterControllers(assemblies).PropertiesAutowired();
builder.RegisterFilterProvider();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));