AutoFac使用

AutoFac是.Net平台下的IOC容器产品,传说是速度最快的一个。

优点:

  1. 它是C#语言联系很紧密,也就是说C#里的很多编程方式都可以为Autofac使用,例如可以用Lambda表达式注册组件
  2. 较低的学习曲线,学习它非常的简单,只要你理解了IoC和DI的概念以及在何时需要使用它们
  3. XML配置支持
  4. 自动装配
  5. 与ASP.Net MVC集成
  6. 微软的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方法说明
  1. builder.RegisterType<Object>().As<Iobject>():注册类型及其实例。例如上面就是注册接口IDataSource的实例Sqlserver
  1. IContainer.Resolve<IDAL>():解析某个接口的实例。例如下面代码,我可以解析接口返回的就是Sqlserver实例

    var builder = new ContainerBuilder();
    //builder.RegisterType<DataSourceManager>();
    builder.RegisterType<Sqlserver>().As<IDataSource>();
    using (var container = builder.Build())
    {
    var manager = container.Resolve<IDataSource>();
    Console.WriteLine(manager.GetData());
    Console.ReadLine();
    }

  2. builder.RegisterType<Object>().Named<Iobject>(string name):为一个接口注册不同的实例。有时候难免会碰到多个类映射同一个接口,比如Sqlerver和Oracle都实现了IDalSource接口,为了准确获取想要的类型,就必须在注册时起名字。

IContainer.ResolveNamed<IDAL>(string name):解析某个接口的“命名实例”。例如上面的实例最后一行代码

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

container.ResolveNamed<IDataSource>("OracelDB");
  1. imagebuilder.RegisterType<Object>().Keyed<Iobject>(Enum enum):以枚举的方式为一个接口注册不同的实例。有时候我们会将某一个接口的不同实现用枚举来区分,而不是字符串。

IContainer.ResolveKeyed<IDAL>(Enum enum):根据枚举值解析某个接口的特定实例。这个和上面的都一样 也就不演示了。

  1. builder.RegisterType<Worker>().InstancePerDependency():用于控制对象的生命周期,每次加载实例时都是新建一个实例,默认就是这种方式。调用的话

    builder.RegisterType<Sqlserver>().Keyed<IDataSource>("Sqlserver").InstancePerDependency();

  2. builder.RegisterType<Worker>().SingleInstance():用于控制对象的生命周期,每次加载实例时都是返回同一个实例

  3. IContainer.Resolve<T>(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<DataSourceManager>()这里通过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));
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,628评论 2 22
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,034评论 1 32
  • 参考:ES6入门(阮一峰)解构:解构与构造数据截然相反。 例如,它不是构造一个新的对象或数组,而是逐个拆分现有的对...
    IceLake阅读 3,867评论 0 2
  • 由宋茜、冯绍峰主演的电视剧《幻城》已经落幕了,剧中宋茜吹弹可破的肌肤,亮眼的外形,动人的舞姿,给人留下深刻印象。这...
    黛颜堂阅读 1,157评论 0 19