Autofac

Autofac

什么是IOC

控制反转(inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)

IOC: 调用者不再创建(不自己new) 被调用者的实例,而是交给容器去创建,这就是控制反转.
DI:容器创建好的实例再注入调用者的过程,就是依赖注入. (比如属性入住构造函数注入等.)

为什么要使用IOC

我们日常使用的设计模式多为接口驱动设计,接口驱动设计有很多好处,可以提供不同灵活的子类实现的,增加代码稳定和健壮性等等.

但是接口一定需要实现的,也就是如下语句迟早要执行:
AInterface 啊= new AInterfaceImp();
所以,按照一般的方法使用接口,即会出现耦合.

构建一个简单的依赖注入的例子

创建服务(接口)

public interface ITestServices
{
        int Count { get; }
        void Add();
}

创建组件(实现类)

public class TestServicesImpl : ITestServices
{
        private int _count;

        public TestServicesImpl()
        {
            _count = 0;
        }

        public int Count => this._count;

        public void Add()
        {
            _count++;
        }
}

创建控制器

[Route("api/TestServices")]
[ApiController]
    public class TestServicesController:ControllerBase
    {
        private readonly ITestServices _testServices;

       // 通过构造函数的形式获取ITestServices服务的注册实例
        public TestServicesController(ITestServices testServices)
        {
            _testServices = testServices;
        }

        [HttpGet]
        public Tuple<int, int> Get()
        {
            var before = this._testServices.Count;   //拿到老的统计值
            this._testServices.Add();                       //自增一个统计值
            var after = this._testServices.Count;      //获取新的统计值
            return new Tuple<int, int>(before,after);//返回老的统计值和新的统计值
        }
    }

在 .Net Core 自带的注入服务的方法中,使用 .Net Core 自带的注入服务进行组件注入


// 在 StartUp类中的  ConfigureServices 方法中进行注入
 public void ConfigureServices(IServiceCollection services)
{
            services.AddControllers();

            //将组件TestServicesImpl,注册给ITestServices 服务
            services.AddScoped<ITestServices, TestServicesImpl>();  
}

IoC 是如何降低耦合的

Class A中用到了Class B 的对象b,一般情况下,需要在A的代码中显式的new一个B的对象.

采用依赖注入技术之后,A的代码值需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类的引用中.二集体获取的方法,对象被获取时的状态由配置(如XML)来指定.

容器

Ioc 模式,系统中通过引入实现了Ioc模式的Ioc容器,即可由Ioc容器来管理对象的生命周期,依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分离.其中一个特点就是通过配置进行应用程序组件间相互关系的配置,而不用重新修改并编写具体的代码.

.Net Core 的常用容器

  1. .Net Core 自带容器
  2. Autofac

.Net 自带容器的使用说明

.Net Core 自带容器的注入一般在Startup中的 public void ConfigureServices(IServiceCollection services) 方法中进行. 根据生命周期区分为三种.

  1. AddTransient<服务,组件>(); //瞬时的,每次使用都会为服务注入一个新的实例.
  2. AddScoped<服务,组件>(); //一次http请求之后使用一个实例,即任何地方调用都是同一个对象.
  3. AddSingle<服务,组件>(); //单例的,在第一次http请求之后会生成一个实例,并且此实例会一直被使用到 .net core 程序生命周期结束,即全局单例.

在 .Net Core 中如果要在Action中,拿到来自注入的实例对象,我们需要使用如下语句,我们可以注意到,在形参中,加入了[FromServices]的标注.

[HttpGet]
public Tuple<int, int> Get([FromServices] ITestServices testServices)
{
            var before = this._testServices.Count;
            this._testServices.Add();
            var after = this._testServices.Count;

            return new Tuple<int, int>(before,after);
}

AutoFac

组件: 一串声明了它所提供服务和它所消费依赖的代码(类,对象)
服务: 一个在提供和消费组件之间明确定义的行为约定(接口)

Autofac 反射组件

RegisterType:注册一个类.
As:将类注册为此接口.
SingleInstance:单例,全局只有一个.
InstanPerLifetimeScope: 在同作用域,服务每次请求只创建一次.

.Net Core 中使用 Autofac 进行依赖注入

我们需要在Nuget上先安装Autofac.Extensions.DependencyInjection,然后我们需要在Program.cs类中,去修改CreateHostBuilder方法,目的是添加Autofac依赖注入容器.

通过Nuget安装Autofac
通过Nuget安装Autofac

//原代码
 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                   .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

//新代码
 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                   .UseServiceProviderFactory(new AutofacServiceProviderFactory())  //添加Autofac 容器
                   .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

我们还需要在StartUp.cs类中添加对Autofac容器进行配置的函数,此处我们需要注意函数的命名规范.

    /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //(注册组件)具体实现的注入(在后续使用中只能对在构造区中定义TestServicesImpl类型的形参进行注入),且不是一次请求只产生一个实例对象,凡是需要注入的地方,都会产生一个新的对象. 
            builder.RegisterType<TestServicesImpl>();    
        }
/// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
           
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //且不是一次请求只产生一个实例对象,凡是需要注入的地方,都会产生一个新的对象.
            builder.RegisterType<TestServicesImpl>().As<ITestServices>();    
        }
 /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //全局单例
            builder.RegisterType<TestServicesImpl>().As<ITestServices>().SingleInstance();
        }
 /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //一次http请求共用一个注入的对象实例
            builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();
        }

以上是,我们通过Autofac实现了, .Net core自带的注入功能,下面我们了解一下Autofac 自己的一些功能.

Autofac指定构造函数

指定构造函数
我们可以通过使用UseingConstructor来指定构造函数来进行注册,此处需要注意一下,如果不能不指定构造函数来进行构造,那么Autofac默认使用形参最多的构造函数来进行实例注入.

声明服务

public interface IConstructor
    {
         ITestServices Services { get; }
    }

声明组件

public class ConstructorImpl:IConstructor
    {
        public ConstructorImpl()
        {
            Console.WriteLine("无参的构造函数");
        }

        private readonly ITestServices _testServices;

        public ConstructorImpl(ITestServices testServices)
        {
            _testServices = testServices;

            Console.WriteLine("有一个参数的构造函数");
        }

        public ITestServices Services => this._testServices;
    }

配置注入

/// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
          
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //一次http请求共用一个注入的对象实例
            builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();

            //不指定构造函数,进行注入时,Autofac默认会使用形参最多的构造函数进行注入.
            builder.RegisterType<ConstructorImpl>().As<IConstructor>();

        }

依赖注入

[Route("api/Autofac")]
    [ApiController]
    public class AutofacController
    {
        private readonly ITestServices _testServices;
        private readonly IConstructor _constructor;

        public AutofacController(ITestServices testServices,IConstructor constructor)
        {
            _testServices = testServices;
            _constructor = constructor;
        }

        [HttpGet]
        public Tuple<int, int> Get([FromServices] ITestServices testServices)
        {
            if (this._testServices.GetHashCode() == testServices.GetHashCode())
            {
                Console.WriteLine(true);
            }
            else
            {
                Console.WriteLine(false);
            }
            var before = this._testServices.Count;
            this._testServices.Add();
            var after = this._testServices.Count;

            return new Tuple<int, int>(before, after);
        }
    }

调用结果:

执行结果
执行结果

通过上述结果可知,Autofac在没有指定构造函数注入时,默认使用形参最多的构造函数进行注入.

使用无参的构造函数进行注入:

 /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //使用无参的构造函数进行注入
            builder.RegisterType<ConstructorImpl>().As<IConstructor>().UsingConstructor();
        }

调用结果:

执行结果
执行结果

Autofac 实例组件

我们可以通过使用 RegisterInstance 来指定实例来进行注册,如果想自己来控制组件的生存周期,可以使用ExternallyOwned

   /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
       
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //一次http请求共用一个注入的对象实例
            builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();

            ////给指定服务注册已有实例 后续使用中,使用的都是同一个实例,即下面new出的实例.
            var instance = new ConstructorImpl();
            builder.RegisterInstance(instance).As<IConstructor>();
        }

Autofac Lambda表达式组件

  1. 可以使用Lambda表达式来进行注入.
  2. 使用Lambda表达式时可以进行复杂参数的使用.
     /// <summary>
        /// Autofac容器配置函数
        /// </summary>
        public void ConfigureContainer(ContainerBuilder builder)
        {
          
            //(注册服务) 对服务进行注册(在后续使用中只能对在构造函数中定义ITestServices接口的形参进行注入),
            //一次http请求共用一个注入的对象实例
            builder.RegisterType<TestServicesImpl>().As<ITestServices>().InstancePerLifetimeScope();

            //使用Lambda表达式进行注入,此处的c表示容器,此处需要注意的是c.Resolve<组件> 这段代码中,此组件必须在下列这行代码被
            //执行前,就已经注册,否则下面的代码就会报错.
            builder.Register(c => new ConstructorImpl(c.Resolve<ITestServices>())).As<IConstructor>();
        }

Autofac 通过参数选择实现

  1. 注册时可以通过parameter参数选择具体的判定参数
  2. 通过ILifetimeScope容器来获取实现.

以上的例子都是一个接口只有一个类型的实现,那么我们一个接口有多种实现类,那么我们是不是可以通过参数,来选择使用某种具体的实现呢?

声明服务

   public interface IPersonService
    {
        string Sex { get; }
    }

声明组件1

public class PersonManImpl : IPersonService
    {
        public string Sex => "男人";
    }

声明组件2

public class PersonWoManImpl:IPersonService
    {

        public string Sex => "女人";
    }

依赖注入

 /// <summary>
 /// Autofac容器配置函数
 /// </summary>
 public void ConfigureContainer(ContainerBuilder builder)
 {
    
     //根据请求参数使用具体的实现类
     //此处c表示容器,p表示参数
     builder.Register<IPersonService>((c, p) =>
     {
         var sex = p.Named<string>("sex");  //通过名称参数来选择具体的实现类
         if (sex == "1")    //1表示男人
         {
             return new PersonManImpl();
         }
         else 
         {
             return new PersonWoManImpl();
         }
     });
 }

调用url : http://localhost:5000/api/autofac/1

调用

[Route("api/Autofac")]
[ApiController]
public class AutofacController
{
    private readonly ILifetimeScope _lifetimeScope;  //保存Autofac容器本身

    public AutofacController( ILifetimeScope lifetimeScope)
    {
        _lifetimeScope = lifetimeScope;
    }


    [HttpGet("{n}")]
    public string GetSex(string n)
    {
        //通过传入的参数来区分到底使用哪个具体的实现.
        var person = this._lifetimeScope.Resolve<IPersonService>(new NamedParameter("sex", n));
        return person.Sex;
    }
}

返回结果:

执行结果
执行结果

Autofac 程序集扫描

  1. RegisterAssemblyTypes:扫描类型.
  2. publicOnly:仅注册公开类型.
  3. Where通过条件过滤.
  4. Except:排除某个类型.

下面我们使用一个CRUD的过程来介绍一下如何使用Autofac.

首先我们需要先通过Nuget来安装两个依赖包.

  1. Autofac Autofac主程序包
  2. Autofac.Extensions.DependencyIniection Autofac扩展的关于DI的包.

通过Nuget安装
通过Nuget安装

在 Porgram.cs 中先添加Autofac容器的支持.

 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())  //添加Autofac 容器
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

在 StartUp.cs 中添加 Autofac 容器的配置函数

/// <summary>
/// Autofac 容器配置函数
/// </summary>
/// <param name="builder"></param>
public void ConfigureContainer(ContainerBuilder builder)
{
            // 获取当前应用程序的所在的程序集
            var curAssembly = Assembly.GetExecutingAssembly();

            // 通过名称来筛选实现, 此处因为我们所有的实现都是以"Impl"来结尾的,所有我们可以通过这个来筛选,并将这些组件,注册给他们自己的服务
            // 此处的t指的是type
            //builder.RegisterAssemblyTypes(curAssembly)
            //       .Where(t => t.Name.EndsWith("Impl"))
            //       .AsImplementedInterfaces();

            //我们也可以将上诉程序集中的所有实现都注册成单例的形式,即
            //builder.RegisterAssemblyTypes(curAssembly)
            //    .Where(t => t.Name.EndsWith("Impl"))
            //    .AsImplementedInterfaces().SingleInstance();

            //我们也可以指定某一个服务,及其实现是单例形式的,代码如下
            builder.RegisterAssemblyTypes(curAssembly)
                .Where(t => t.Name.EndsWith("Impl") && t.Name != "RepositoryImpl")  //过滤处RepositoryImpl
                .AsImplementedInterfaces().SingleInstance();

            //上面的所有已Impl结尾的都是单例的除了(RepositoryImpl),下面是挑出不想是单例的实现,单独注册
            builder.RegisterAssemblyTypes(curAssembly)
                .Where(t => t.Name.EndsWith("RepositoryImpl"))
                .AsImplementedInterfaces();


                // 其它功能请参阅 Aufofac文档
 }

下面我们使用仓储模式简单的进行设计一下.

创建仓储接口(服务):

using System.Collections.Generic;
using MyIoCDemo.Model;

namespace MyIoCDemo.Repository
{
    public interface IUserRepository
    {
        IEnumerable<UserModel> GetUsers();
        UserModel GetUser(int id);
        void AddOrUpdateUser(UserModel user);
        void DeleteUser(int id);
    }
}

创建数据模型

namespace MyIoCDemo.Model
{
    public class UserModel
    {
        public int Id { get; set; }
        public int Age { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }

    }
}

创建仓储实现类(组件):

using System.Collections.Generic;
using System.Linq;
using MyIoCDemo.Model;

namespace MyIoCDemo.Repository
{
    public class UserRepositoryImpl:IUserRepository
    {
        private List<UserModel> _users = new List<UserModel>
        {
            new UserModel
            {
                Id = 1,Age = 20,Name = "张三",Phone="1234567"
            },
            new UserModel
            {
                Id = 2,Age = 21,Name = "李四",Phone="1234568"
            },
            new UserModel
            {
                Id = 3,Age = 22,Name = "王五",Phone="1234569"
            }
        };

        public IEnumerable<UserModel> GetUsers()
        {
            return this._users;
        }

        public UserModel GetUser(int id)
        {
            var item = this._users.Find(u => u.Id == id);
            return item;
        }

        public void AddOrUpdateUser(UserModel user)
        {
            var item = this._users.Where(u => u.Id == user.Id).FirstOrDefault();
            if (item == null)
            {
                //添加
                this._users.Add(user);
            }
            else
            {
                //更新
                this._users.Remove(item);
                this._users.Add(user);
            }
        }

        public void DeleteUser(int id)
        {
            var item = this._users.Where(u => u.Id == id).FirstOrDefault();
            if (item != null)
            {
                //删除
                this._users.Remove(item);
            }
           
        }
    }
}

创建用户接口(服务):

using System.Collections.Generic;
using MyIoCDemo.Model;

namespace MyIoCDemo.Services
{
    public interface IUserServices
    {
        IEnumerable<UserModel> GetUsers();
        UserModel GetUser(int id);
        void AddOrUpdateUser(UserModel user);
        void DeleteUser(int id);
    }
}

创建用户实现类(组件):

using System.Collections.Generic;
using MyIoCDemo.Model;
using MyIoCDemo.Repository;

namespace MyIoCDemo.Services
{
    public class UserServicesImpl:IUserServices
    {
                              
        private readonly IUserRepository _repository;

        public UserServicesImpl(IUserRepository repository)
        {
            _repository = repository;
        }

        public IEnumerable<UserModel> GetUsers()
        {
            return this._repository.GetUsers();
        }

        public UserModel GetUser(int id)
        {
            return this._repository.GetUser(id);
        }

        public void AddOrUpdateUser(UserModel user)
        {
            this._repository.AddOrUpdateUser(user);
        }

        public void DeleteUser(int id)
        {
            this._repository.DeleteUser(id);
        }
    }
}

控制器:

using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.AspNetCore.Mvc;
using MyIoCDemo.Model;
using MyIoCDemo.Services;

namespace MyIoCDemo.Controllers
{
    [Route("api/User")]
    [ApiController]
    public class UserController:ControllerBase
    {
        private readonly IUserServices _userServices;

        public UserController(IUserServices userServices)
        {
            _userServices = userServices;
        }

        /// <summary>
        /// 获取所有员工
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public IEnumerable<UserModel> Get()
        {
            return this._userServices.GetUsers();
        }

        /// <summary>
        /// 获取指定员工
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("{id}")]
        public UserModel Get(int id)
        {
            return this._userServices.GetUser(id);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="model"></param>
        [HttpPost]
        public void Post([FromBody]UserModel model)
        {
            this._userServices.AddOrUpdateUser(model);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="id"></param>
        /// <param name="value"></param>
        [HttpPut("{id}")]
        public void Put(int id, [FromBody] UserModel value)
        {
            value.Id = id;
            this._userServices.AddOrUpdateUser(value);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="id"></param>
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
            this._userServices.DeleteUser(id);
        }
    }
}

以上例子参考自:

.Net Core 依赖注入基础
.Net Core Autofac 基础1
.Net Core Autofac 基础2

posted @ 2020-03-24 18:10  HelloZyjS  阅读(473)  评论(0编辑  收藏  举报