依赖注入框架Autofac类型注册与简单使用

Autofac

    Autofac是一套高效的依赖注入框架。

    Autofac官方网站:http://autofac.org/

    Autofac在Github上的开源项目:https://github.com/autofac/Autofac

    Autofac安装:通过VS的Nuget可以很方便的获取。

IoC/DI

    关于IoC与DI的概念,网上有很多相关的博客,大家可以稍微了解一下,对比一下。

    我个人的理解就是按照英文的中文翻译来理解的:

    IoC: Inversion of Control 控制反转,将控制权进行反转,将本由自身控制的对象初始化交由外部IoC容器进行初始化;

    DI: Dependency Injection 依赖注入,将对象依赖的其他对象,通过注入的方式进行初始化。

 
简单示例

    我们先通过一个简单的例子来看看Autofac的基本使用方式以及一些概念:
    

    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Class_1>();    //将Class_1注册到Autofac容器中

            IContainer container = builder.Build();
            Class_1 obj = container.Resolve<Class_1>(); //从autofac容器中获取Class_1对象
            Console.WriteLine(obj.Id);

            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    
    

    class Class_1
    {
        public Guid Id { get; set; }

        public Class_1()
        {
            Id = Guid.NewGuid();
        }
    }

    

    上面的代码演示了最简单注册/获取方式。我们先通过ContainerBuilder进行类型注册,然后在后面可以通过IContainer进行获取类型实例。这里我们也明白了一点,类型是需要先进行注册,然后才能够通过Autofac进行获取(后面会谈到一些特殊的配置,但是这也属于另类的注册方式)。

    然后我们能知道在Autofac中,先通过ContainerBuilder进行类型注册,然后通过ContainerBuilder的Build方法来获取IContainer类型实例,后面则可以通过该IContainer实例来获取注册类型的实例对象。

 
Autofac类型注册

    类型/泛型注册

    在简单实例中,我们已经看到了一种注册方式,那就是泛型注册:

    builder.RegisterType<Class_1>();

    这种注册方式很方便也很简单,比较常用,但是有一个缺点就是,注册的类型必须在当前项目或被当前项目引用,因为使用泛型,必须类型明确。针对这点,还有一种通过Type对象进行注册的方式:

    //字符串为类型完全名称
    builder.RegisterType(Type.GetType("AutofacBlog.Class_1"));

    使用这种方式进行类型注册,被注册的类型可以不是被直接引用,但是注意,类型所在的程序集必须被加载。

    这种注册方式,在有插件或类似需要动态加载程序集的情况下比较使用,通过扫描程序集,获取一些满足指定条件的类型,来进行注册。

     
程序集批量注册

    类型注册中提到了通过扫描程序集,来获取部分类型进行注册。Autofac对此提供了一个方便的方式,可以直接通过程序集来筛选类型注册:

    //获取当前应用程序加载程序集(C/S应用中使用)
    var assembly = Assembly.GetExecutingAssembly();
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(assembly); //注册所有程序集类定义的非静态类型

    上述代码中,通过RegisterAssemblyTypes方法,将assembly中所有自定义的非静态类型都注册到Autofac中,后面可以使用IContainer对象获取所有该程序集中自定义的类型对象。

    这种方式达到了批量的效果,但是通常,我们并不需要把所有的类型都进行注册,所以Autofac提供了几种过滤方式:

    builder.RegisterAssemblyTypes(assembly)
        .Where(type => type.Namespace.Equals("IocAutofac.Example")); //条件判断

    这种方式相信大家都比较熟悉,Where+lambda的方式来进行条件判断,lambda参数为Type类型,也就是程序集中的type。

    builder.RegisterAssemblyTypes(assembly)
        .Except<Program>();  //排除Program类型

    这种方式用来排除指定类型的注册,当排除的个例比较少时,会比较适用。Except还有另一种用法,但是用法比较复杂,在此不进行介绍。

    builder.RegisterAssemblyTypes(assembly)
        .InNamespace("IocAutofac.Example");  //类型在IocAutofac.Example命名空间中

    被注册的类型需要在指定命名空间中。

    builder.RegisterAssemblyTypes(assembly)
        .InNamespaceOf<Program>();  //类型在Program所在的命名空间中*/

    这种方式与上面一种方式比较相似,也是用来判断命名空间的,这种方式是根据类型来获取命名空间。

     

    通过这种方式,我们可以对程序集中的类型进行批量注册,类型/泛型方式在被注册类型较少的情况下还是不错的,但当被注册类型很多的时候,一个一个的手写注册会显得很无力,这时候就是程序集批量注册显威的时候了。

     
 Lambda注册

    上面讲到的两种方式都是通过类型进行直接注册的,这种注册方式,在获取时,会直接通过构造函数new出对象(关于构造函数的优选选择在后面的博文中将进行说明),不会做更多的操作。有时,我们希望能够在获取对象时能够自动的做更多的事情时,我们可以通过Lambda注册来解决:
    

    builder.Register(cc =>
    {
        var clas1 = new Class_1();
        while (clas1.Id.ToString().Contains("a"))
        {
            clas1.Id = Guid.NewGuid();
        }
        return clas1;
    });

    

    上述代码,实际注册了Class_1类型,因为最后返回的对象类型为Class_1。

    Register方法接收了一个lambda表达式作为参数,在lambda表达式中,我们可以做很多事,包括一些属性注入(后续说明)、方法注入(后续说明)、条件判断等等。

    我们在通过Autofac获取Class_1对象时,实际会执行这样的一个表达式。另外,lambda表达式的参数cc的类型是IComponentContext,这里我们可以简单的当做IContainer进行使用。

     
实例注册

    var clas1 = new Class_1();
    clas1.Id = Guid.Empty;

    builder.RegisterInstance(clas1);

    通过RegisterInstance进行实例注册,进行实例注册时,我们需要注意,实例注册可以作为一种单例注册的方式,也就是在后面通过Autofac获取Class_1对象时,获取到的是注册时的那个对象。并且,如果一个在某处修改了该对象,其他地方再获取时,获取到的就是修改后的对象。

     
    Module注册

    在日常开发中,可能不同开发会负责不同的模块进行单独开发。在开发过程中,不同模块不同开发可能都有自己的类型需要注册到autofac中,但是如果每个人在注册时,都去修改一个指定地方的代码,这在进行代码合并时,是令人痛苦的。更好的方式是,每个开发不同的模块都有自己指定的类型注册区域,这样,在代码合并时,会减少很多代码冲突。

    对于这种方式,Autofac已经为我们提供了:
    

    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();

            builder.RegisterModule<ModuleA>();
            //这两种注册方式达到的效果都一样
            builder.RegisterModule(new ModuleB());

            IContainer container = builder.Build();
            Class_1 clas1 = container.Resolve<Class_1>();
            Class_2 clas2 = container.Resolve<Class_2>();
            Console.WriteLine(clas1.Id);
            Console.WriteLine(clas2.ToString());

            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    class ModuleA : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<Class_1>();
        }
    }

    class ModuleB : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<Class_2>();
        }
    }

    

    class Class_2
    {

    }

    上述代码中,有两个继承自Module类的类:ModuleA、ModuleB,这两个类型重写了父类的Load方法,并在load方法中,分别注册了Class_1与Class_2类型。然后在主程序中,通过RegisterModule对Module进行注册。

    通过这种方式,不同的开发就可以各自创建一个类继承自Module,然后重写Load方法,在Load方法进行自己的类型注册,最后再进行Module的统一注册。

        Module注意说明

        实际上,RegisterModule需要的参数,并不是继承自Module类的,而是实现了IModule接口的类,而Module也是实现了IModule接口的。也就是我们也可以写一个实现了IModule接口的类型,然后在RegisterModule时传入。但是一般我们直接去继承Module就好了,这种方式比较简单方便,实现IModule的方式更为复杂,当然,功能也更多,在此就不进行介绍了。

     
程序集Module注册

    Module注册,为多人开发提供了一种方便的注册方式,但是我们也可以发现,这种方式,还是会需要手动注册Module,如果Module过多,Module注册代码也会显得多而杂,当然,可以通过人工管理来控制Module的量。但是Autofac还提供了一种更方便的方式,并且,对于类似Orchard的模块开发(子模块与主模块无引用关系,通过程序集加载方式来加载子模块)或是插件开发,我们没办法通过Registerodule来注册无直接引用关系的Module。

    对于上述的情况,Autofac提供了很好的方式来解决:

    var builder = new ContainerBuilder();
    var assembly = Assembly.GetExecutingAssembly();
    builder.RegisterAssemblyModules(assembly);

    上述代码会注册assembly程序集中所有实现了IModule接口的类型(多层继承也算),这样,我们只需要将取出所有程序集,然后通过RegisterAssemblyModules进行一次性注册,就可以自动注册所有Module了。

    RegisterAssemblyModule还可以指定一个泛型类型:

    builder.RegisterAssemblyModules<ModuleA>(assembly);

    这样注册,是指定只注册assembly程序集中继承自ModuleA的Module。

 
尾述

    本文主要讲述Autofac中比较常用也比较好用的注册方式,并没有把所有的注册方式都讲述出来。

    个人非常推荐使用Module,每个项目拥有自己的一个Module,这样的一个Module都有固定的未知,便于查找该项目中的注册及依赖关系。

    通过本文,可以了解到几种常用的注册方式,以及最简单的对象获取方式,初学者可以简单的试试。

依赖注入框架Autofac的简单使用

1)解压它的压缩包,主要看到Autofac.dll,Autofac.Configuration.dll,这也是本篇文章重点使用的Autofac的类库。

2)创建一个控制台工程,并且引用以上的DLL文件。创建一个数据库操作接口IDatabase.cs:

/// <summary>
/// Database operate interface
/// </summary>
public interface IDatabase
{
string Name { get; }

void Select(string commandText);

void Insert(string commandText);

void Update(string commandText);

void Delete(string commandText);
}

这里包含CRUD四种操作的方法。

 

3)创建两种数据库的操作类,SqlDatabase.cs以及OracleDatabase.cs:

public class SqlDatabase : IDatabase
{
    public string Name
    {
        get { return "sqlserver"; }
    }

    public void Select(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a query sql in {1}!", commandText, Name));
    }

    public void Insert(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a insert sql in {1}!", commandText, Name));
    }

    public void Update(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a update sql in {1}!", commandText, Name));
    }

    public void Delete(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a delete sql in {1}!", commandText, Name));
    }
}

以及

public class OracleDatabase : IDatabase
{
    public string Name
    {
        get { return "oracle"; }
    }

    public void Select(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a query sql in {1}!", commandText, Name));
    }

    public void Insert(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a insert sql in {1}!", commandText, Name));
    }

    public void Update(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a update sql in {1}!", commandText, Name));
    }

    public void Delete(string commandText)
    {
        Console.WriteLine(string.Format("'{0}' is a delete sql in {1}!", commandText, Name));
    }
}

 
4)接着创建一个数据库管理器DatabaseManager.cs:

 
public class DatabaseManager
{
    IDatabase _database;

    public DatabaseManager(IDatabase database)
    {
        _database = database;
    }

    public void Search(string commandText)
    {
        _database.Select(commandText);
    }

    public void Add(string commandText)
    {
            _database.Insert(commandText);
    }

    public void Save(string commandText)
    {
            _database.Update(commandText);
    }

    public void Remove(string commandText)
    {
            _database.Delete(commandText);
    }

}

 

5)在控制台中,编写以下测试程序:

var builder = new ContainerBuilder();
builder.RegisterType<DatabaseManager>();
builder.RegisterType<SqlDatabase>().As<IDatabase>();
using (var container = builder.Build())
{
    var manager = container.Resolve<DatabaseManager>();
    manager.Search("SELECT * FORM USER");
}

 

运行结果:

分析:

这里通过ContainerBuilder方法RegisterType对DatabaseManager进行注册,当注册的类型在相应得到的容器中可以Resolve你的DatabaseManager实例。

builder.RegisterType<SqlDatabase>().As<IDatabase>();通过AS可以让DatabaseManager类中通过构造函数依赖注入类型相应的接口。

Build()方法生成一个对应的Container实例,这样,就可以通过Resolve解析到注册的类型实例。

 

同样地,如果你修改数据库类型注册为:

builder.RegisterType<OracleDatabase>().As<IDatabase>();

运行结果:

 

6)显然以上的程序中,SqlDatabase或者OracleDatabase已经暴露于客户程序中了,现在我想将该类型选择通过文件配置进行读取。Autofac自带了一个Autofac.Configuration.dll 非常方便地对类型进行配置,避免了程序的重新编译。

修改App.config:

<configuration>
  <configSections>
    <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
  </configSections>
  <autofac defaultAssembly="AutofacDemo">
    <components>
      <component type="AutofacDemo.SqlDatabase, AutofacDemo" service="AutofacDemo.IDatabase" />
    </components>
  </autofac>
</configuration>

通过Autofac.Configuration.SectionHandler配置节点对组件进行处理。

对应的客户端程序改为:

var builder = new ContainerBuilder();
builder.RegisterType<DatabaseManager>();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
using (var container = builder.Build())
{
    var manager = container.Resolve<DatabaseManager>();
    manager.Search("SELECT * FORM USER");
}

运行结果:

7)另外还有一种方式,通过Register方法进行注册:

var builder = new ContainerBuilder();
//builder.RegisterType<DatabaseManager>();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.Register(c => new DatabaseManager(c.Resolve<IDatabase>()));
using (var container = builder.Build())
{
    var manager = container.Resolve<DatabaseManager>();
    manager.Search("SELECT * FORM USER");
}

得到结果也是一样的。

 

8)现在我想通过一个用户类来控制操作权限,比如增删改的权限,创建一个用户类:

/// <summary>
/// Id Identity Interface
/// </summary>
public interface Identity
{
    int Id { get; set; }
}

public class User : Identity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

修改DatabaseManager.cs代码:

public class DatabaseManager
{
    IDatabase _database;
    User _user;

    public DatabaseManager(IDatabase database) : this(database, null)
    {
    }

    public DatabaseManager(IDatabase database, User user)
    {
        _database = database;
        _user = user;
    }

    /// <summary>
    /// Check Authority
    /// </summary>
    /// <returns></returns>
    public bool IsAuthority()
    {
        bool result = _user != null && _user.Id == 1 && _user.Name == "leepy" ? true : false;
        if (!result)
            Console.WriteLine("Not authority!");

        return result;
    }

    public void Search(string commandText)
    {
        _database.Select(commandText);
    }

    public void Add(string commandText)
    {
        if (IsAuthority())
            _database.Insert(commandText);
    }

    public void Save(string commandText)
    {
        if (IsAuthority())
            _database.Update(commandText);
    }

    public void Remove(string commandText)
    {
        if (IsAuthority())
            _database.Delete(commandText);
    }
}

在构造函数中增加了一个参数User,而Add,Save,Remove增加了权限判断。

修改客户端程序:

User user = new User { Id = 1, Name = "leepy" };
var builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.RegisterInstance(user).As<User>();
builder.Register(c => new DatabaseManager(c.Resolve<IDatabase>(), c.Resolve<User>()));

using (var container = builder.Build())
{
    var manager = container.Resolve<DatabaseManager>();

    manager.Add("INSERT INTO USER ...");
}

运行结果:

分析:

builder.RegisterInstance(user).As<User>();注册User实例。

builder.Register(c => new DatabaseManager(c.Resolve<IDatabase>(), c.Resolve<User>()));通过Lampda表达式注册DatabaseManager实例。

如果这里我修改User的属性值:

User user = new User { Id = 2, Name = "zhangsan" };

运行结果:

说明该用户无权限操作。

时间: 2024-10-22 09:05:25

依赖注入框架Autofac类型注册与简单使用的相关文章

依赖注入框架Autofac的简单使用

     Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上也是很高的.于是,今天抽空研究了下它.下载地址:http://code.google.com/p/autofac/downloads/list http://www.cnblogs.com/liping13599168/archive/2011/07/16/2108209.html http://www.cnblogs.com/hkncd/archive

Dagger——Android 的依赖注入框架

简介 在开发程序的时候,会用到各种对象,很多对象在使用之前都需要进行初始化.例如你要操作一个SharedPreference,你需要调用getSharedPreferences(String name,int mode)来获取一个对象,然后才能使用它.而如果这个对象会在多个Activity中被使用,你就需要在每个使用的场景中都写下同样的代码.这不仅麻烦,而且增加了出错的可能.dagger的用途就是:让你不需要初始化对象.换句话说,任何对象声明完了就能直接用. 原理 dagger是使用依赖注入的方

ABP框架的基础配置及依赖注入讲解_基础应用

配置ABP配置是通过在自己模块的PreInitialize方法中来实现的 代码示例如下: public class SimpleTaskSystemModule : AbpModule { public override void PreInitialize() { //在你的应用中添加语言包,这个是英语和作者的土耳其语. Configuration.Localization.Languages.Add(new LanguageInfo("en", "English"

.Net框架搭建之2、SQL Server MEF依赖注入 MVC Repository框架

MEF依赖注入简介 依赖注入对于开发人员来说,方便的就是不需要去关注具体的实现类,不需要去New实例对象,直接使用接口层就能让程序自动注入使用,当然,还有其他一些特点,比如web http同一个请求中可以设置同一个对象只实例化一次解决多个类中多次实例化对象浪费资源的问题.不多说,百度能得到更多 多的介绍,这边直接开工搭环境. 1.数据模型Model层创建 数据模型层,首先要创建数据库,再创建Model类. 创建数据库,表,添加一条测试数据 创建数据库 创建数据表 添加测试数据 我们已经知道有几层

NopCommerce架构分析(一)Autofac依赖注入类生成容器_自学过程

NopCommerce为了实现松耦合的框架设计目的,使用了IOC框架:Autofac.据有人测试,Autofac是性能很好的IOC工具. 1.在IOC中,组件首先需要在IOC中注册,有通过配置文件注册的.像Spring.net,也有通过特性注册的,像StructureMap,也有通过代理来注册的,像Autofac.但是IOC讲究一个原则,就是接口和实现分离.所有IOC就是生命某个具体类实现了某个接口.然后在使用时,系统从IOC中获取接口的实现类,并创建对象. 2.下面来看NopCommerce如

ASP.NET Core中的依赖注入(3): 服务的注册与提供

在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core中的DI容器体现为一个实现了IServiceProvider接口的对象. ServiceProvider与ServiceDescriptor 服务的注册与提供     利用ServiceProvider来提供服务     提供一个服务实例的集合     获取ServiceProvider自身对象  

AngularJS 依赖注入详解和简单实例_AngularJS

AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分. 该模式分离了客户端依赖本身行为的创建,这使得程序设计变得松耦合,并遵循了依赖反转和单一职责原则.与服务定位器模式形成直接对比的是,它允许客户端了解客户端如何使用该系统找到依赖 一句话 --- 没事你不要来找我,有事我会

ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内容相对分散和零碎,我们有必要针对这个主题作一个归纳性的介绍.采用依赖注入的服务均由某个ServiceProvider来提供,但是在ASP.NET Core管道涉及到两个不同的ServiceProvider,其中一个是在管道成功构建后创建并绑定到WebHost上的ServiceProvider,对应着

解读ASP.NET 5 &amp; MVC6系列(7):依赖注入

原文:解读ASP.NET 5 & MVC6系列(7):依赖注入 在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Injection),ASP.NET 5正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程序,MVC6也利用了依赖注入的功能重新对Controller和View的服务注入功能进行了重新设计:未来的依赖注入功能还可能提供更多的API,所有如果还没有开始接触依赖注入的话,就得好好学一下了. 在之前版本的依赖注入功能里,依赖注入的