IOC
就是我们需要一个对象 以前我们是去 new 现在我们是直接向 IOC容器 要我们需要的那个对象。
使用一个IOC容器(autofac)通过依赖注入控制各个组件的耦合。也就是说你写好了组件,不需要你去自己控制他们的依赖关系,哪个类又持有哪个类的对象,哪个类里面又要声明一个对象,而是把他们都放到一个容器里面,容器替你做这个(把组件组合起来)。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。
DI
由容器动态的将某个依赖关系注入到组件之中 就是 一个对象a 需要去访问数据库 以前我们是自己编写代码去创建一个connction对象 来连接对象,现在我们只需要用@autowrite来获取connection对象,在系统运行时,IOC容器会在合适的时候去创建这个对象,对象a不必关心connection是怎么创建的,何时创建的,这就完成了对各个对象关系的控制
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
谁依赖于谁:当然是 应用程序依赖于IoC容器
为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源
谁注入谁:很明显是 IoC容器注入应用程序某个对象,应用程序依赖的对象
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)
Net Core 三个常用的生命周期
首先,我们想象一个这样一个场景。假设我们有寄快递的需求,那么我们会致电快递公司:“我们要寄快递,派一个快递员过来收货”。接着,快递公司会如何做呢?
- 一直派遣同一个快递员来收货。
- 第一周派遣快递员A、第二周派遣快递员B收货。
- 每次都派遣一个新的快递员收货。
1.Transient(瞬态模式):每一次GetService都会创建一个新的实例 瞬间生命周期 : 每次需要创建一个全新的 每次请求时都会创建的瞬时生命周期服务。这个生命周期最适合轻量级,无状态的服
2.Scoped(作用域模式):在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内) 单例进程唯一实例 :每一次请求都是同一个(多用于时间) 在同作用域,服务每个请求只创建一次。
3.Singleton(单例模式):整个应用程序生命周期内只创建一个实例 全局只创建一次,第一次被请求的时候被创建,然后就一直使用这一个.
如何使用这三种生命周期呢?.我们直接在注入的时候用不同的方法就行了,代码如下:
//注册不同生命周期的服务
services.AddTransient<ITestService, TestService>();
services.AddScoped<ITestService2, TestService2>();
services.AddSingleton<ITestService3, TestService3>();
自带的IOC 做一些小的项目完全没有问题,但是大项目使用起来就比较单一
IOC容器替换为Autofac
AutoFac生命周期
InstancePerLifetimeScope:作用域模式 在一个嵌套语句块中,只会返回一个实例。 在解决每个生命周期实例作用域组件时,每个嵌套作用域将获得一个实例(例如,每个工作单元)。相当于AddScope
SingleInstance:单例模式,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;相当于AddSingleton
InstancePerDependency:瞬态模式 默认模式,每次调用,都会重新实例化对象;每次请求都创建一个新的对象;AddTransient
Autofac可完美替换系统的依赖注入功能,可实现构造函数注入和属性注入,替换过程:
在Program.cs 新增一行代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())//集成Autofac
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
然后在Startup.cs 增加方法
//集成Autofac
public void ConfigureContainer(ContainerBuilder builder)
{
//添加依赖注入实例,AutofacModuleRegister就继承自Autofac.Module的类
builder.RegisterModule(new StartupHelp.AutofacModuleRegister());
}
ConfigureAutofac 是自己封装的一个类 继承了 Autofac.Module
其中注册类必须继承自Autofac.Module,并且在Load中实现注入,此处应用了程序集范围的注入以及单个对象的注入,最后添加的代码是为了支持控制器的属性注入。
注入的接口(如IUnitOfWork)和实现(UnitOfWork)没有特别。
注入对象的生命周期与.netcore内置的一致
public class AutofacRegister : Autofac.Module
{
#region 单个类和接口
//直接注册某一个类和接口
//左边的是实现类,右边的As是接口
//containerBuilder.RegisterType<TestServiceE>().As<ITestServiceE>().SingleInstance();
//这里就跟默认DI差不多
//services.AddScoped<TestServiceE, ITestServiceE>();
#endregion #region 方法1 Load 适用于无接口注入
//var assemblysServices = Assembly.Load("Exercise.Services"); //containerBuilder.RegisterAssemblyTypes(assemblysServices)
// .AsImplementedInterfaces()
// .InstancePerLifetimeScope(); //var assemblysRepository = Assembly.Load("Exercise.Repository"); //containerBuilder.RegisterAssemblyTypes(assemblysRepository)
// .AsImplementedInterfaces()
// .InstancePerLifetimeScope(); #endregion #region 方法2 选择性注入 与方法1 一样
// Assembly Repository = Assembly.Load("Exercise.Repository");
// Assembly IRepository = Assembly.Load("Exercise.IRepository");
// containerBuilder.RegisterAssemblyTypes(Repository, IRepository)
//.Where(t => t.Name.EndsWith("Repository"))
//.AsImplementedInterfaces().PropertiesAutowired(); // Assembly service = Assembly.Load("Exercise.Services");
// Assembly Iservice = Assembly.Load("Exercise.IServices");
// containerBuilder.RegisterAssemblyTypes(service, Iservice)
//.Where(t => t.Name.EndsWith("Service"))
//.AsImplementedInterfaces().PropertiesAutowired();
#endregion #region 方法3 使用 LoadFile 加载服务层的程序集 将程序集生成到bin目录 实现解耦 不需要引用
//获取项目路径
//var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
//var ServicesDllFile = Path.Combine(basePath, "Exercise.Services.dll");//获取注入项目绝对路径
//var assemblysServices = Assembly.LoadFile(ServicesDllFile);//直接采用加载文件的方法
//containerBuilder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces(); //var RepositoryDllFile = Path.Combine(basePath, "Exercise.Repository.dll");
//var RepositoryServices = Assembly.LoadFile(RepositoryDllFile);//直接采用加载文件的方法
//containerBuilder.RegisterAssemblyTypes(RepositoryServices).AsImplementedInterfaces();
#endregion #region 在控制器中使用属性依赖注入,其中注入属性必须标注为public
//public ITestServiceE _testService {get;set }
//注意 上方为属性注入 发现为Null 需要在Startup.cs 的 ConfigureServices 方法下加入如下代码
//services.AddControllers().AddControllersAsServices(); //在控制器中使用属性依赖注入,其中注入属性必须标注为public
// var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
//.Where(type => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(type)).ToArray();
// containerBuilder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired();
#endregion /// <summary>
/// 当前使用
/// </summary>
/// <param name="builder"></param>
protected override void Load(ContainerBuilder builder)
{
//程序集范围注入 将匹配所有Service结尾的
builder.RegisterAssemblyTypes(typeof(SysUserService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().PropertiesAutowired();
//单个注册 工作单元 和数据库上下文
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().PropertiesAutowired();
builder.RegisterType<dbContext>().As<DbContext>().PropertiesAutowired(); //在控制器中使用属性依赖注入,其中注入属性必须标注为public 就是不需要通过构造函数 直接
//public ITestServiceE _testService {get;set } 可直接_testService.方法 不许在构造函数接收
//注意属性注入 发现为Null 需要在Startup.cs 的 ConfigureServices 方法下加入如下代码
//services.AddControllers().AddControllersAsServices();
var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
.Where(type => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(type)).ToArray();
builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired();
}
}
然后控制器通过构造函数注入,或者属性注入测试