ASP.NET Core能够说是到处皆注入,本文从基础角度理解一下原生DI容器,及介绍下怎么使用而且如何替换官方提供的默认依赖注入容器。html
百度百科中对于依赖注入的定义:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,能够用来减低计算机代码之间的耦合度。其中最多见的方式叫作依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。经过控制反转,对象在被建立的时候,由一个调控系统内全部对象的外界实体将其所依赖的对象的引用传递给它。也能够说,依赖被注入到对象中。
web
那么在依赖反转以前或者叫控制反转以前,直接依赖是怎么工做的呢,这里ClassA直接依赖ClassB,而ClassB又直接依赖ClassC,任何一处的变更都会牵一发而动全身,不符合软件工程的设计原则。
编程
应用依赖关系反转原则后,A 能够调用 B 实现的抽象上的方法,让 A 能够在运行时调用 B,而 B 又在编译时依赖于 A 控制的接口(所以,典型的编译时依赖项发生反转) 。 运行时,程序执行的流程保持不变,但接口引入意味着能够轻松插入这些接口的不一样实现。
依赖项反转是生成松散耦合应用程序的关键一环,由于能够将实现详细信息编写为依赖并实现更高级别的抽象,而不是相反 。 所以,生成的应用程序的可测试性、模块化程度以及可维护性更高。 遵循依赖关系反转原则可实现依赖关系注入 。app
若是你用过Spring,就知道其庞大而全能的生态圈正是由于有了它包含各类各样的容器来作各类事情,其本质也是一个依赖反转工厂,那么不知道你注意到没有,控制反转后产生依赖注入,这样的工做咱们能够手动来作,那么若是注入的服务成千上万呢,那怎么玩呢?那么问题来了,控制反转了,依赖的关系也交给了外部,如今的问题就是依赖太多,咱们须要有一个地方来管理全部的依赖,这就是容器的角色。
容器的主要职责有两个:绑定服务与实例之间的关系(控制生命周期)。获取实例并对实例进行管理(建立和销毁)。框架
在.Net Core里提供了默认的依赖注入容器IServiceCollection,它是一个轻量级容器。核心组件为两个IServiceCollection和IServiceProvider,IServiceCollection负责注册,IServiceProvider负责提供实例。
使用两个核心组件前导入命名空间Microsoft.Extensions.DependencyInjection.
默认的ServiceCollection有如下三个方法:asp.net
IServiceCollection serviceCollection=new ServiceCollection(); #三个方法都是注册实例,只不过实例的生命周期不同。 #单例模式,只有一个实例 serviceCollection.AddSingleton<ILoginService, EFLoginService>(); #每次请求都是同一个实例,好比EntityFramework.Context serviceCollection.AddScoped<ILoginService, EFLoginService>(); #每次调用都是不一样的实例 serviceCollection.AddTransient<ILoginService, EFLoginService>();
#接口声明 public interface IServiceCollection : IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable { }
#默认的ServiceCollection其实是一个提供了ServiceDescriptor的List。 public class ServiceCollection : IServiceCollection, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable, IList<ServiceDescriptor> { private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>(); public int Count { get { return this._descriptors.Count; } } public bool IsReadOnly { get { return false; } } public ServiceDescriptor this[int index] { get { return this._descriptors[index]; } set { this._descriptors[index] = value; } } public void Clear() { this._descriptors.Clear(); } public bool Contains(ServiceDescriptor item) { return this._descriptors.Contains(item); } public void CopyTo(ServiceDescriptor[] array, int arrayIndex) { this._descriptors.CopyTo(array, arrayIndex); } public bool Remove(ServiceDescriptor item) { return this._descriptors.Remove(item); } public IEnumerator<ServiceDescriptor> GetEnumerator() { return (IEnumerator<ServiceDescriptor>) this._descriptors.GetEnumerator(); } void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item) { this._descriptors.Add(item); } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator) this.GetEnumerator(); } public int IndexOf(ServiceDescriptor item) { return this._descriptors.IndexOf(item); } public void Insert(int index, ServiceDescriptor item) { this._descriptors.Insert(index, item); } public void RemoveAt(int index) { this._descriptors.RemoveAt(index); } }
三个方法对应的生命周期值,在枚举ServiceLifeTime中定义:ide
public enum ServiceLifetime { Singleton, Scoped, Transient, }
三个方法确切来讲是定义在扩展方法ServiceCollectionServiceExtensions中定义:模块化
#这里我列出个别方法,详细可参看源码 #导入Microsoft.Extensions.DependencyInjection public static class ServiceCollectionServiceExtensions { public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Type implementationType) { if (services == null) throw new ArgumentNullException(nameof (services)); if (serviceType == (Type) null) throw new ArgumentNullException(nameof (serviceType)); if (implementationType == (Type) null) throw new ArgumentNullException(nameof (implementationType)); //这里注入时指定ServiceLifetime.Transient return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Transient); } public static IServiceCollection AddScoped( this IServiceCollection services, Type serviceType, Type implementationType) { if (services == null) throw new ArgumentNullException(nameof (services)); if (serviceType == (Type) null) throw new ArgumentNullException(nameof (serviceType)); if (implementationType == (Type) null) throw new ArgumentNullException(nameof (implementationType)); //这里注入时指定ServiceLifetime.Scoped return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Scoped); } public static IServiceCollection AddSingleton( this IServiceCollection services, Type serviceType, Type implementationType) { if (services == null) throw new ArgumentNullException(nameof (services)); if (serviceType == (Type) null) throw new ArgumentNullException(nameof (serviceType)); if (implementationType == (Type) null) throw new ArgumentNullException(nameof (implementationType)); //这里注入时指定ServiceLifetime.Singleton return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Singleton); } }
ASP.NET Core在Startup.ConfigureService中注入指定服务,能够从方法参数IServiceCollection中看出,这里还有个方法services.AddMvc(), 这个MVC框架自己本身注入的服务,定义在MvcServiceCollectionExtesnsions类中。函数
#Startup public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<ILoginService, EFLoginService>(); }
官方推荐在构造器中注入,这里也是为了体现显示依赖。测试
public class AccountController { private ILoginService _loginService; public AccountController(ILoginService loginService) { _loginService = loginService; } }
前面提到原生的依赖注入容器只是一个轻量级容器,可是功能真的颇有限,好比属性注入、方法注入、子容器、lazy对象初始化支持。为什么很差好借鉴一下Spring强大的背景呢,因此这里咱们用Autofac替换系统默认的依赖注入容器。先引用命名空间Autofac、Autofac.Extensions.DependencyInjection。我本机环境使用的.Net Core3.0。 3.0不能修改直接修改Startup的ConfigureService方法了,直接修改ConfigureService方法返回值会抛出异常ConfigureServices returning an System.IServiceProvider isn't supported. 这里能够参考Autofac文档,已经有说明。
#直接声明方法ConfigureContainer public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddMvc(); } public void ConfigureContainer(ContainerBuilder containerBuilder) { containerBuilder.RegisterType<EFLoginService>().As<ILoginService>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }
#导入命名空间Autofac.Extensions.DependencyInjections,而后调用UseServiceProviderFactory public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults( webBuilder => { webBuilder.UseStartup<Startup>(); }) .UseServiceProviderFactory(new AutofacServiceProviderFactory()); }
https://docs.microsoft.com/zh-cn/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion
http://www.javashuo.com/article/p-fzebuzum-y.html
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html