当前位置:坤哥网-kungge-Autofac学习笔记:通过一个例子学会Autofac基本原理与使用

Autofac学习笔记:通过一个例子学会Autofac基本原理与使用

2017/11/22 14:48:06 kungge阅读(123) 评论(0)


在面向对象程序设计中,封装、继承、多态是面向的三个基本特征,设计模式的应用是对面向对象思想的一种体现,在设计模式中的几大设计原则中有个原则叫依赖倒转(置)原则(DIP),其思想强调的是“抽象不应依赖于细节,细节应依赖于抽象”,即要多面向接口编程而非具体实现类编程。由DIP衍生出了控制反转(IoC)、IoC容器和依赖注入(DI)这三个概念,它们到底有什么关系呢?我们下面捋一捋。

依赖倒转(DIP):指的是一种软件设计原则。

控制反转(IoC):DIP的一种实现方式,使用控制反转的思想可以让开发者不用将类绑定在应用里,而是在类的构造方法中将依赖传递进去。

依赖注入(DI):IoC的一种实现方式,用来反转依赖。

IoC容器:DI框架,用来映射依赖,管理对象的创建和其生命周期。


今天的主角Autofac就是一种IoC框架,IoC框架有很多种,之前的文章介绍了Unity,原理大致相同。Autofac在.net平台使用很广泛,性能很不错,有人做过性能测试,据说是速度排第一。下面让我们了解并使用它。

Autofac是一个开源框架,源代码公开在github上,有兴趣的可以研究研究:https://github.com/autofac/Autofac


简单使用Autofac


假设有这样一个场景项目的数据库可以有多种,随时都有可能切换到不同的数据库,新增数据时采用的数据校验方式也会不一样,对此我们在这里模拟一个新增订单的情况,订单类:

public class Order
{
   public int Id { get; set; }
   public decimal Amount { get; set; }
}

数据库帮助接口:

public interface IDBHelper
{
    bool Insert<T>(T t);
}

SqlServer帮助类,实现IDBHelper接口:

    public class SqlServerHelper : IDBHelper
    {
        private IValidateData _validate;
        public SqlServerHelper(IValidateData validate)
        {
            this._validate = validate;
        }
        public bool Insert<T>(T t)
        {
            if (this._validate.Validate(t))
            {
                Console.WriteLine($"{this.GetType().Name}插入一条记录");
                return true;
            }
            else
            {
                return false;
            }
        }
    }

MySql帮助类,实现IDBHelper接口:

public class MySqlHelper:IDBHelper
    {
        private IValidateData _validate;
        public MySqlHelper(IValidateData validate)
        {
            this._validate = validate;
        }
        public bool Insert<T>(T t)
        {
            if (this._validate.Validate(t))
            {
                Console.WriteLine($"{this.GetType().Name}插入一条记录");
                return true;
            }
            else
            {
                return false;
            }
        }
    }

数据校验接口和对应的类:

public interface IValidateData
{
       bool Validate<T>(T t);
}
public class FirstValidate : IValidateData
{
        public bool Validate<T>(T t)
        {
            Console.WriteLine("第一种验证方式!");
            return true;
        }
}
public class SecondValidate: IValidateData
{
        public bool Validate<T>(T t)
        {
            Console.WriteLine("第二种验证方式!");
            return true;
        }
}


  1. 新建一个控制台应用程序,通过NuGet添加Autofac引用:

22fbf6ec-a55c-42f0-b043-5ca3a4b124bd.png

2.应用启动的时候,需要添加一个ContainerBuilder,通过它来注册组件,组件可以是一个表达式、.NET 类型或者其他暴露一个或多个服务的一段代码, 同时它也可以引入其他的依赖。要将上面的例子实现,我们需要注册相关的类,并且暴露这些类的接口,在Build时返回一个容器,我们要将容器保存下来。

代码如下:

        private static IContainer Container { get; set; }
        static void Main(string[] args)
        {
                var builder=new ContainerBuilder();
                builder.RegisterType<FirstValidate>().As<IValidateData>();
                builder.RegisterType<MySqlHelper>().As<IDBHelper>();
                Container = builder.Build();

                Console.ReadKey();
        }

上面的代码和Unity还是挺像的,不了解Unity的同学可以看看我之前关于Unity的相关文章。

3.容器本身有一个生命周期,我们可以直接通过容器解析组件:

private static IContainer Container { get; set; }
static void Main(string[] args)
{
        var builder=new ContainerBuilder();
        builder.RegisterType<FirstValidate>().As<IValidateData>();
        builder.RegisterType<MySqlHelper>().As<IDBHelper>();
        using(Container = builder.Build())
        {
                var sqlHelper = scope.Resolve<IDBHelper>();
                Order order = new Order();
                order.Amount = 5888;
                sqlHelper.Insert(order);
        }
        Console.ReadKey();
}

但实际一般不会这样做,为什么呢?因为容器在应用程序生命周期中一直是存在的,如果直接通过容器解析组件,结束时会有一堆的东西等待释放,有可能会造成内存泄漏。实际情况一般是从容器中创建一个子生命周期,再在子生命周期中解析,当组件解析完成时,释放掉子生命周期,其他的全部会被清理。因此我们这样做:

class Program
{
        private static IContainer Container { get; set; }
        static void Main(string[] args)
        {
                var builder=new ContainerBuilder();
                builder.RegisterType<FirstValidate>().As<IValidateData>();
                builder.RegisterType<MySqlHelper>().As<IDBHelper>();
                Container = builder.Build();

                Order order = new Order();
                order.Amount = 5888;
                Insert<Order>(order);

                Console.ReadKey();
        }
        static void Insert<T>(T t)
        {
            using (var scope = Container.BeginLifetimeScope())
            {
                var sqlHelper = scope.Resolve<IDBHelper>();
                sqlHelper.Insert(t);
            }
        }
}

运行输出:

2c43b9f4-756c-40ab-95f0-9f33eea2b6b3.png


上面的程序运行过程解析:

  1. Insert方法向Autofac请求一个IDBHelper。

  2. Autofac发现IDBHelper对应MySqlHelper,于是开始创建MySqlHelper实例。

  3. Auofac发现MySqlHelper在其构造函数中需要一个IValidateData。

  4. Autofac发现IValidata对应FirstValidate,于是开始创建FirstValidate实例。

  5. Autofac使用创建的FirstValidate实例完成MySqlHelper的创建。

  6. Autofac返回完整的MySqlHelper给Insert方法使用。


在实际开发者,不会这样四处人为地创建生命周期,一般使用Autofac集成类库方式将其集成进项目中来实现。后面的文章会介绍,欢迎关注。


标签: Autofac

发表评论 没有账号,注册评论