The story of Xiao Wang

Xiao Wang goes to work

Xiao Wang is a programmer. He goes to work every weekday. His favorite means of transportation is riding a tram. Emptying himself during the ride made him fast. Suddenly one day the weather forecast said that there would be heavy rain in the near future. It was obviously impossible for Xiao Wang to go to work by tram. Then he will choose to go by car. But because Xiao Wang relies too much on riding an electric car every day, his driving skills are limited, so he has to learn to drive again. So Xiao Wang is very upset, I want to go to a class, but also to master different means of transportation, this is really annoying, do I want to be a simple worker is so difficult? Wang told this to his wife, her wife is an old driver, she said, “You don’t need to take care of this matter, I am an old driver, I can control this matter, you just remember to buy me more gifts.” From then on xiao Wang set his mind at making money for his wife, driving this thing is completely in charge of his wife.

Xiao Wang’s Diary:

I have enjoyed myself to work control), go to work every day is take out my beloved electric vehicles (instantiate objects), until the day of heavy rain the weather forecast told me, to rely on bicycle will be heavy rain, and I found that I’m going to learn driving again in transport (high coupling), know she is my wife’s adult said old driver (ioc container), She controls the driving (inversion of control), no matter how she gets to work, she just tells her in advance what means of transportation (DI) she takes me to work every weekday when I call my wife.


Dependency injection

We can see some key words dependency and control from wang’s story and today we are going to talk about what dependency injection is. When it comes to dependency injection (DI), there is a concept called inversion of control (IOC). IOC — Inversion of Control is not a technology, but a design idea. The idea is to design dependent classes to hand over control to the container, rather than instantiating control directly in the object. The IOC container dynamically provides an object with other objects it depends on while the system is running. His implementation is to use dependency injection to achieve. DI – Dependency Injection is the Dependency between components determined by the container. To do this, we need to understand dependency and injection relationships dependency: applications rely on the IOC container because they need it to provide resources outside of the object. Injection: The external resources required by an object (including objects, resources, and constant data) are injected into the IOC container.


Net5 built-in dependency injection

Dependency injection is the core concept of NET Core and Net5, and we need to have a clear idea of how it is implemented in our framework after we understand the concept. Of course, official documents are the best way to find out. Next, I would like to talk about the understanding of DEPENDENCY injection in NET 5 (NET Core) from my perspective. First of all, we need to know that the implementation of NET5 dependency injection is made up of net built-in containers and third-party containers. We’ll focus on the built-in implementation.

DependencyInjection

Net5’s built-in dependency injection is mostly usedMicrosoft.Extensions.DependencyInjectionandMicrosoft.Extensions.DependencyInjection.AbstractionTwo quotes. Nuget references will do. They areOpen source.We can see that DependencyInjection is not a big project, but it is the foundation of the entire net5(NET Core) because it provides the default implementation of the DependencyInjection container, which is the Core foundation of net5(NET Core).

The source code parsing

IServiceCollection

public class ServiceCollection : IServiceCollection
{
     //ServiceDescriptor Cache set
     private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
     // Register the number of ServiceDescriptors in the current ServiceCollection object
     public int Count => _descriptors.Count;
     public bool IsReadOnly => false;
     public ServiceDescriptor this[int index]
     {
         get
         {
             return _descriptors[index];
         }
         set
         {
             _descriptors[index] = value; }}// Clear all ServiceDescriptor objects
     public void Clear() 
     {
         _descriptors.Clear();
     }
     // Query whether the ServiceCollection contains the specified ServiceDescriptor object
     public bool Contains(ServiceDescriptor item)=> _descriptors.Contains(item);
     / / copy ServiceDescriptor
     public void CopyTo(ServiceDescriptor[] array, int arrayIndex) =>_descriptors.CopyTo(array, arrayIndex);
     // Remove the specified ServiceDescriptor from the ServiceCollection
     public bool Remove(ServiceDescriptor item)=>_descriptors.Remove(item);
     // Get an iterator for this ServiceCollection
     public IEnumerator<ServiceDescriptor> GetEnumerator()=> _descriptors.GetEnumerator();
     public int IndexOf(ServiceDescriptor item) => _descriptors.IndexOf(item);
     public void Insert(int index, ServiceDescriptor item) => _descriptors.Insert(index, item);
     public void RemoveAt(int index)=> _descriptors.RemoveAt(index);
}
Copy the code

IServiceCollection&&ServiceDescriptor

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    ///A convention for specifying a collection of service descriptors
    /// </summary>
    public interface IServiceCollection : IList<ServiceDescriptor> {}}Copy the code

The built-in container is ServiceProvider. We will inject our services into ServiceProvider. IServiceCollection is a collection of ServiceProvider lists.

ServiceDescriptor

     public static IServiceCollection Add(this IServiceCollection collection,IEnumerable<ServiceDescriptor> descriptors)
        {
            if (collection == null)
            {
                throw new ArgumentNullException(nameof(collection));
            }
            if (descriptors == null)
            {
                throw new ArgumentNullException(nameof(descriptors));
            }
            foreach (var descriptor in descriptors)
            {
                collection.Add(descriptor);
            }
            return collection;
        }
Copy the code

We can see that when the service is registered, it provides a ServiceDescriptor

 /// <summary>
    /// Describes a service with its service type, implementation, and lifetime.
    /// </summary>
    [DebuggerDisplay("Lifetime = {Lifetime}, ServiceType = {ServiceType}, ImplementationType = {ImplementationType}")]
    public class ServiceDescriptor
    {}
Copy the code

It is a description of the type of service, its life cycle, and how it is obtained. type

// The lifecycle of the registered type
public ServiceLifetime Lifetime { get; }
/ / base types
public Type ServiceType { get; }
// Instance type (derived type)
public Type ImplementationType { get; }
// Instance object
public object ImplementationInstance { get; }
// Register type instantiates the factory of the object
public Func<IServiceProvider, object> ImplementationFactory { get; }
Copy the code

The constructor

// Derived type
public ServiceDescriptor(Type serviceType,object instance)
     : this(serviceType, ServiceLifetime.Singleton)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationInstance = instance;
}
 / / factory
public ServiceDescriptor(Type serviceType,Func<IServiceProvider, object> factory,ServiceLifetime lifetime)
     : this(serviceType, lifetime)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationFactory = factory;
}
// Concrete instance object
public ServiceDescriptor(Type serviceType,Type implementationType,ServiceLifetime lifetime)
     : this(serviceType, lifetime)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationType = implementationType;
}
Copy the code
/// <summary>
///Gets the instance type of the current registered type (inner class)
/// </summary>
/// <returns></returns>
internal Type GetImplementationType(){}
// Methods that actually instantiate objects, overloads call such methods
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime){}
public static ServiceDescriptor Singleton(Type serviceType,object implementationInstance){}
public static ServiceDescriptor Scoped(Type service, Func<IServiceProvider, object> implementationFactory){}
Copy the code

ServiceCollectionServiceExtensions&ServiceCollectionDescriptorExtensions&ServiceCollectionContainerBuilderExtensions

These three methods are implemented by three extension methods of ServiceCollection: we use the registration method; TryAdd and RemoveAll,Replace and other operations and constructsServiceProviderInstance. ServiceCollectionServiceExtensions: too much code (registration) of different life cycle is not posted, cut a figure.ServiceCollectionDescriptorExtensions:

public static void TryAdd(this IServiceCollection collection,ServiceDescriptor descriptor)
{
     if(! collection.Any(d => d.ServiceType == descriptor.ServiceType)) collection.Add(descriptor); }Copy the code

TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable, TryAdd, TryAddEnumerable ServiceCollectionContainerBuilderExtensions

    public static class ServiceCollectionContainerBuilderExtensions
    {
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services)     
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)    
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)}Copy the code

IServiceProvider

public sealed class ServiceProvider : IServiceProvider.IDisposable.IServiceProviderEngineCallback
{
    // Extended interface of ServiceProvider
    // Use subclasses of this interface to make calls to cache various registration services and call visitor objects to get instance objects
     private readonly IServiceProviderEngine _engine;  
     ///Cache type (Visitor pattern)
     private readonly CallSiteValidator _callSiteValidator;
     internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
     {
          IServiceProviderEngineCallback callback = null;
          if (options.ValidateScopes)
          {
               callback = this;
               _callSiteValidator = new CallSiteValidator();
          }
          // Instantiate the work engine type
          switch (options.Mode)
          {
               case ServiceProviderMode.Dynamic:
                    / / instantiate DynamicServiceProviderEngine
                    _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                    break;
               case ServiceProviderMode.Runtime:
                    _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                    break;
                    case ServiceProviderMode.ILEmit:
                    _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
               break;
                    case ServiceProviderMode.Expressions:
                    _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
                    break;
               default:
               	throw new NotSupportedException(nameof(options.Mode)); }}// Gets the service object of the specified type
     public object GetService(Type serviceType) => _engine.GetService(serviceType);
     public void Dispose() => _engine.Dispose();
    // Create a service instance cache
	void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
       =>_callSiteValidator.ValidateCallSite(callSite);
    // Service instance validation
     void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
      =>_callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
}
Copy the code

We can see an instance of the service that ServiceProvider uses to get the service, which provides an extension IServiceProviderEngine to do this. X let’s take a look at the source code

public static class ServiceProviderServiceExtensions
{
	 // Obtain the T service from IServiceProvider.
     public static T GetService<T> (this IServiceProvider provider)
          => (T)provider.GetService(typeof(T));
     
     // Obtain the T service from IServiceProvider.
     public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
     {
          // If the Current ServiceProvider implements ISupportRequiredService
          // Call GetRequiredService of ServiceProvier to obtain the service instance
          var requiredServiceSupportingProvider = provider as ISupportRequiredService;
          if(requiredServiceSupportingProvider ! =null)
             return requiredServiceSupportingProvider.GetRequiredService(serviceType);
          // If the ServiceProvider does not implement ISupportRequiredService
          // Call GetService directly to get the service instance, but throw an exception if the service instance is empty
          var service = provider.GetService(serviceType);
          if (service == null)
              throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
          return service;
     }
     public static T GetRequiredService<T> (this IServiceProvider provider)
          => (T)provider.GetRequiredService(typeof(T));
     // Get all service instances of the specified registration type 
      
     public static IEnumerable<T> GetServices<T> (this IServiceProvider provider)
          => provider.GetRequiredService<IEnumerable<T>>();
     // Get all service instances of the specified registration type 
      
     public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType)
     {
          // Create an IEnumberable<> set of type serviceType, with type serviceTypele as the generic parameter to the current set
          var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
          return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
     }

     // Create a child IServiceProvider instance
     public static IServiceScope CreateScope(this IServiceProvider provider)
          => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
Copy the code

NET built-in dependency injection implementation

We cannot Startup. ConfigureServices (IServiceCollection services) to register directly I use code below look at implementation

public class UserService : IUserService
{
    public string GetName()
    {
        return "Wang Yanling"; }}public interface IUserService
{
    string GetName();
}

Copy the code

1. Interface oriented registration

services.AddScoped(typeof(IUserService), typeof(UserService));
services.AddScoped<IUserService, UserService>();
Copy the code

2. Implementation form registration

services.AddScoped<UserService>();
services.AddScoped(typeof(UserService));
Copy the code

Both forms work, but extensibility is obviously the first.

3. Batch injection

var assembly = Assembly.GetExecutingAssembly()
                        .DefinedTypes
                        .Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I"));
foreach (var item in assembly)
{
    services.AddScoped(item.GetInterfaces().FirstOrDefault(), item);
}
Copy the code

Third-party container (Autofact) injection

I’ll write a separate article later.