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 的常用容器
- .Net Core 自带容器
- Autofac
.Net 自带容器的使用说明
.Net Core 自带容器的注入一般在Startup中的 public void ConfigureServices(IServiceCollection services) 方法中进行. 根据生命周期区分为三种.
- AddTransient<服务,组件>(); //瞬时的,每次使用都会为服务注入一个新的实例.
- AddScoped<服务,组件>(); //一次http请求之后使用一个实例,即任何地方调用都是同一个对象.
- 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依赖注入容器.
//原代码
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表达式组件
- 可以使用Lambda表达式来进行注入.
- 使用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 通过参数选择实现
- 注册时可以通过parameter参数选择具体的判定参数
- 通过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 程序集扫描
- RegisterAssemblyTypes:扫描类型.
- publicOnly:仅注册公开类型.
- Where通过条件过滤.
- Except:排除某个类型.
下面我们使用一个CRUD的过程来介绍一下如何使用Autofac.
首先我们需要先通过Nuget
来安装两个依赖包.
- Autofac Autofac主程序包
- Autofac.Extensions.DependencyIniection Autofac扩展的关于DI的包.
在 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