アウトライン
ASP.NETコアは、射出どこでも言うことができる、我々は、ネイティブDIコンテナの下基盤の観点から、それを使用する方法を理解し、デフォルトと公式に提供注射コンテナに依存を交換する方法について説明します。
依存性注入とは何ですか
Baiduの百科事典依存注入定義:制御の反転(と略記制御の反転、IOCは)、あるオブジェクト指向プログラミング 1つの設計原理が間コンピュータコード減少させるために使用することができる結合の程度を。最も一般的なメソッドが呼び出される依存性注入(と呼ばれる、依存性注入DI)、「依存のルックアップ」(依存関係のルックアップ)と呼ばれる方法があります。オブジェクトは、それに渡されたオブジェクトに依存する調節システム内のすべてのオブジェクトに対して外部のエンティティによって作成された制御の反転による。それは対象依存に注入され、言うことができます。
依存逆転前
前その後、依存逆転または制御の反転前に呼び出され、ここで、それがどのように動作するかに直接依存しているにClassA ClassBの、そしてClassBの直接従属クラスCに直接依存して、任意の変更は、ソフトウェアを満たしていない、全体としてプル状況のいずれかになりますエンジニアリング設計原理。
依存関係反転後
依存性逆転原理を適用した後、Aは、Aが実行時にBを呼び出すことができるように、達成する抽象Bのメソッドを呼び出すと、Bと、コンパイル時にインタフェースA制御に依存する(したがって、典型的な依存関係がコンパイル時に発生することができ)逆。プログラムのランタイム実行の流れは同じままであるが、簡単にこれらのインタフェースの異なる実装をプラグインすることができることはインタフェース手段の導入。
実装の詳細に依存し、より高い抽象レベルではなく、逆を達成するために書くことができるので、依存関係反転は、疎結合アプリケーションにキー要素を生成します。したがって、テスタビリティが生成されたアプリケーション、モジュール性と保守性の高度。依存性注入を実現することができる依存性逆転の原則に従ってください。
何コンテナ
如果你用过Spring,就知道其庞大而全能的生态圈正是因为有了它包含各种各样的容器来做各种事情,其本质也是一个依赖反转工厂,那么不知道你注意到没有,控制反转后产生依赖注入,这样的工作我们可以手动来做,那么如果注入的服务成千上万呢,那怎么玩呢?那么问题来了,控制反转了,依赖的关系也交给了外部,现在的问题就是依赖太多,我们需要有一个地方来管理所有的依赖,这就是容器的角色。
容器的主要职责有两个:绑定服务与实例之间的关系(控制生命周期)。获取实例并对实例进行管理(创建和销毁)。
ASP.NET Core里依赖注入是怎么实现的
在.Net Core里提供了默认的依赖注入容器IServiceCollection,它是一个轻量级容器。核心组件为两个IServiceCollection和IServiceProvider,IServiceCollection负责注册,IServiceProvider负责提供实例。
使用两个核心组件前导入命名空间Microsoft.Extensions.DependencyInjection.
默认的ServiceCollection有以下三个方法:
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中定义:
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中初始化
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文档,已经有说明。
修改Startup
#直接声明方法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?}");
});
}
}
修改Program
#导入命名空间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
https://www.cnblogs.com/loogn/p/10566510.html
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html