In-depth understanding of ASP.NET Core dependency injection

image.png

Outline

       ASP.NET Core can be said everywhere injection, we understand how to use it from the perspective of the foundation under native DI container, and describes how to replace the default and dependent on official provided injection container.

What is Dependency Injection

       Baidu Encyclopedia dependence injection definition: Inversion of Control (Inversion of Control, abbreviated as the IoC ), is an object-oriented programming one design principles can be used to reduce the computer code between the degree of coupling . The most common method is called dependency injection (Dependency Injection, referred to as DI ), there is a way called "dependent lookup" (Dependency Lookup). By inversion of control, when the object was created by an outside entity for all objects within a regulatory system on which it depends object passed to it. It can be said, it is injected into the subject dependency.

Dependent reversal ago

Before then called before the dependent reversal or inversion of control, is directly dependent on how it works, here is directly dependent ClassA ClassB, and ClassB and directly dependent ClassC, any change will be one of the pull situation as a whole, does not meet the software engineering design principles.
image.png

After the Dependency Inversion

After applying the dependency inversion principle, A can invoke methods on abstract B to achieve, so that A can call B at runtime, and B and at compile time depends on the interface A control (and therefore, the typical dependency occurs at compile time reverse). Runtime execution flow of the program remains the same, but the introduction of the interface means that you can easily plug in different implementations of these interfaces.
image.png
Dependency inversion is generated loosely coupled applications a key element, because it can be written to depend on implementation details and to achieve a higher level of abstraction, rather than the reverse. Therefore, testability generated applications, higher degree of modularity and maintainability. Follow the dependency inversion principle may be realized dependency injection.

What container

       如果你用过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

Guess you like

Origin www.cnblogs.com/sword-successful/p/12329198.html