The ASP.NET Core ServiceProvider

Foreword

In ASP.NET Core , Microsoft provides a set of default dependency injection implementation that corresponds to the package: Microsoft.Extensions.DependencyInjectionwe can look at by looking at their corresponding open source warehouse its implementation. Based on this realization, we do not have to explicitly create our clients, it can be injected into a unified ServiceProvider the centralized maintenance, use the time to get directly in the object. Let us at the time of writing business logic, not too concerned about the creation and destruction of objects. This is why now some best practices recommended not to overuse New ways to get the object. In this article, we will together look at how to implement its own ServiceProvider .

living comfortably without anybody's help

In order to facilitate the distinction, I have here a custom-defined class called: ServiceLocator , its function and official ServiceProvider similar.

Basically

First, we need to define a simple service discovery interface for the upper bound by a particular realization, the sample code is as follows:

public interface IServiceLocator
{
    void AddService<T>();
    T GetService<T>();
}

Next, we define a succession of said interface implementation class, the following sample code:

public class ServiceLocator : IServiceLocator
{
    private readonly IDictionary<object, object> services;

    public ServiceLocator()
    {
        services = new Dictionary<object, object>();
    }

    public void AddService<T>()
    {
        services.TryAdd(typeof(T), Activator.CreateInstance<T>());
    }

    public T GetService<T>()
    {
        try
        {
            return (T)services[typeof(T)];
        }
        catch (KeyNotFoundException)
        {
            throw new ApplicationException("The requested service is not registered");
        }
    }
}

Thus, we implemented a basic service discovery classes, we can service our more centralized management through the class. For convenience here, I simulated three service classes for registration, sample code is as follows:

public interface IService
{
    void SayHello();
}
public class ServiceA : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from A");
    }
}
public class ServiceB : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from B");
    }
}
public class ServiceC : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from C");
    }
}

Use is very simple, as follows:

class Program
{
    static void Main(string[] args)
    {
        IServiceLocator locator = new ServiceLocator();
        locator.AddService<ServiceA>();
        locator.AddService<ServiceB>();
        locator.AddService<ServiceC>();


        locator.GetService<ServiceA>().SayHello();
        locator.GetService<ServiceB>().SayHello();
        locator.GetService<ServiceC>().SayHello();
    }
}

Run results as shown below:

Program seems to work well, the results are in line with our expectations. But little work experience will find friends to achieve the above there are many potential problems. For IServiceLocatorinstance, we generally will be carried out using the Singleton pattern, which will be designed to thread safety commission, so our list of services must if the thread-safe. In addition, if too many of our services that require registration, be performed by the Register, then it will be added to the system overhead because our service Once registered will immediately go is initialized, thereby unnecessarily consuming system memory, so we should let examples of delayed instantiated only operate when in use. Here we improve these questions one by one.

Singleton

Singleton is the simplest but also the most frequently used design pattern Singleton pattern itself in many forms, interested can see my previous blog post: Design Patterns series - single-case model , here, I used a thread-safe manner modify our ServiceLocator, in addition, we also need to modify our services for the collection classes thread-safe type. Therefore, the entire modification, the sample code is as follows:

public class ServiceLocator : IServiceLocator
{
    private static ServiceLocator _instance;

    private static readonly object _locker = new object();

    public static ServiceLocator GetInstance()
    {
        if (_instance == null)
        {
            lock (_locker)
            {
                if (_instance == null)
                {
                    _instance = new ServiceLocator();
                }
            }
        }

        return _instance;
    }

    private readonly IDictionary<object, object> services;

    private ServiceLocator()
    {
        services = new ConcurrentDictionary<object, object>();
    }

    public void AddService<T>()
    {
        services.TryAdd(typeof(T), Activator.CreateInstance<T>());
    }

    public T GetService<T>()
    {
        try
        {
            return (T)services[typeof(T)];
        }
        catch (KeyNotFoundException)
        {
            throw new ApplicationException("The requested service is not registered");
        }
    }
}

Lazy loading

To get all the registered service support lazy loading, we need to introduce a new collection, this new set is used to store our instance of the corresponding object at the time of registration we only record registration type, need access to the appropriate when serving, we just need to visit the set list in this example, if we need to find the service has not been instantiated, then we re-instantiated, then stored instantiate the object and return. What kind of data structure used to save for, we can use a variety of data structures, I am here still use the dictionary to store the sample code as follows:

public class ServiceLocator : IServiceLocator
{
    private static ServiceLocator _instance;

    private static readonly object _locker = new object();

    public static ServiceLocator GetInstance()
    {
        if (_instance == null)
        {
            lock (_locker)
            {
                if (_instance == null)
                {
                    _instance = new ServiceLocator();
                }
            }
        }
        return _instance;
    }

    private readonly IDictionary<Type, Type> servicesType;
    private readonly IDictionary<Type, object> instantiatedServices;

    private ServiceLocator()
    {
        servicesType = new ConcurrentDictionary<Type, Type>();
        instantiatedServices = new ConcurrentDictionary<Type, object>();
    }

    public void AddService<T>()
    {
        servicesType.Add(typeof(T), typeof(T));
    }

    public T GetService<T>()
    {
        if (!instantiatedServices.ContainsKey(typeof(T)))
        {
            try
            {
                ConstructorInfo constructor = servicesType[typeof(T)].GetConstructor(new Type[0]);
                Debug.Assert(constructor != null, "Cannot find a suitable constructor for " + typeof(T));
                T service = (T)constructor.Invoke(null);

                instantiatedServices.Add(typeof(T), service);
            }
            catch (KeyNotFoundException)
            {
                throw new ApplicationException("The requested service is not registered");
            }
        }

        return (T)instantiatedServices[typeof(T)];
    }
}

Since matching structure

All the improvements above all no-argument constructor of support services, but the services have registered for participation constructor, we define the service provider is not satisfied, because the above reflection mode is not supported there arg constructor. In this case we have two solutions. The first is the initialization of the service into the top, and then through a ServiceLocator Fun to get the way of example, and store it, we call it 显示创建. The second way is still by way of reflection, but this reflection may look complicated, we call it 隐式创建. We were for the two implementations of the code examples.

  • Display configuration
public interface IServiceLocator
{
    void AddService<T>();

    //新增接口
    void AddService<T>(Func<T> Implementation);

    T GetService<T>();
}

public class ServiceLocator : IServiceLocator
{
    private static ServiceLocator _instance;

    private static readonly object _locker = new object();

    public static ServiceLocator GetInstance()
    {
        if (_instance == null)
        {
            lock (_locker)
            {
                if (_instance == null)
                {
                    _instance = new ServiceLocator();
                }
            }
        }
        return _instance;
    }

    private readonly IDictionary<Type, Type> servicesType;
    private readonly IDictionary<Type, object> instantiatedServices;

    private ServiceLocator()
    {
        servicesType = new ConcurrentDictionary<Type, Type>();
        instantiatedServices = new ConcurrentDictionary<Type, object>();
    }

    public void AddService<T>()
    {
        servicesType.Add(typeof(T), typeof(T));
    }

    //新增接口对应的具体实现
    public void AddService<T>(Func<T> Implementation)
    {
        servicesType.Add(typeof(T), typeof(T));
        var done = instantiatedServices.TryAdd(typeof(T), Implementation());
        Debug.Assert(done, "Cannot add current service: " + typeof(T));
    }

    public T GetService<T>()
    {
        if (!instantiatedServices.ContainsKey(typeof(T)))
        {
            try
            {
                ConstructorInfo constructor = servicesType[typeof(T)].GetConstructor(new Type[0]);
                Debug.Assert(constructor != null, "Cannot find a suitable constructor for " + typeof(T));
                T service = (T)constructor.Invoke(null);

                instantiatedServices.Add(typeof(T), service);
            }
            catch (KeyNotFoundException)
            {
                throw new ApplicationException("The requested service is not registered");
            }
        }
        return (T)instantiatedServices[typeof(T)];
    }
}

-------------------------------------------------------------------------------------------

public interface IService
{
    void SayHello();
}
public class ServiceA : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from A");
    }
}
public class ServiceB : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from B");
    }
}
public class ServiceC : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from C");
    }
}

public class ServiceD : IService
{
    private readonly IService _service;

    public ServiceD(IService service)
    {
        _service = service;
    }
    public void SayHello()
    {
        Console.WriteLine("-------------");
        _service.SayHello();
        Console.WriteLine("Hello,I'm from D");
    }
}

-------------------------------------------------------------------------------------------

class Program
{
    static void Main(string[] args)
    {
        IServiceLocator locator = ServiceLocator.GetInstance();
        locator.AddService<ServiceA>();
        locator.AddService<ServiceB>();
        locator.AddService<ServiceC>();

        locator.GetService<ServiceA>().SayHello();
        locator.GetService<ServiceB>().SayHello();
        locator.GetService<ServiceC>().SayHello();

        locator.AddService(() => new ServiceD(locator.GetService<ServiceA>()));
        locator.GetService<ServiceD>().SayHello();
    }
}

Program output as shown below:

When we need to have a registered service corresponding parameter in the constructor parameters do not need to register ServiceLocator , then we can use this method of service registration, more flexible.

  • Implicit construction
public class ServiceLocator : IServiceLocator
{
    private static ServiceLocator _instance;

    private static readonly object _locker = new object();

    public static ServiceLocator GetInstance()
    {
        if (_instance == null)
        {
            lock (_locker)
            {
                if (_instance == null)
                {
                    _instance = new ServiceLocator();
                }
            }
        }
        return _instance;
    }

    private readonly IDictionary<Type, Type> servicesType;
    private readonly IDictionary<Type, object> instantiatedServices;

    private ServiceLocator()
    {
        servicesType = new ConcurrentDictionary<Type, Type>();
        instantiatedServices = new ConcurrentDictionary<Type, object>();
    }

    public void AddService<T>()
    {
        servicesType.Add(typeof(T), typeof(T));
    }
    public void AddService<T>(Func<T> Implementation)
    {
        servicesType.Add(typeof(T), typeof(T));
        var done = instantiatedServices.TryAdd(typeof(T), Implementation());
        Debug.Assert(done, "Cannot add current service: " + typeof(T));
    }

    public T GetService<T>()
    {
        var service = (T)GetService(typeof(T));
        if (service == null)
        {
            throw new ApplicationException("The requested service is not registered");
        }
        return service;
    }

    /// <summary>
    /// 关键代码
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private object GetService(Type type)
    {
        if (!instantiatedServices.ContainsKey(type))
        {
            try
            {
                ConstructorInfo constructor = servicesType[type].GetTypeInfo().DeclaredConstructors
                                            .Where(constructor => constructor.IsPublic).FirstOrDefault();
                ParameterInfo[] ps = constructor.GetParameters();

                List<object> parameters = new List<object>();
                for (int i = 0; i < ps.Length; i++)
                {
                    ParameterInfo item = ps[i];
                    bool done = instantiatedServices.TryGetValue(item.ParameterType, out object parameter);
                    if (!done)
                    {
                        parameter = GetService(item.ParameterType);
                    }
                    parameters.Add(parameter);
                }

                object service = constructor.Invoke(parameters.ToArray());

                instantiatedServices.Add(type, service);
            }
            catch (KeyNotFoundException)
            {
                throw new ApplicationException("The requested service is not registered");
            }
        }
        return instantiatedServices[type];
    }
}

-------------------------------------------------------------------------------------------

public interface IService
{
    void SayHello();
}
public class ServiceA : IService
{
    public void SayHello()
    {
        Console.WriteLine("Hello,I'm from A");
    }
}
public class ServiceD : IService
{
    private readonly ServiceA _service;

    public ServiceD(ServiceA service)
    {
        _service = service;
    }
    public void SayHello()
    {
        Console.WriteLine("-------------");
        _service.SayHello();
        Console.WriteLine("Hello,I'm from D");
    }
}

-------------------------------------------------------------------------------------------

class Program
{
    static void Main(string[] args)
    {
        IServiceLocator locator = ServiceLocator.GetInstance();
        locator.AddService<ServiceD>();
        locator.AddService<ServiceA>();
        locator.GetService<ServiceD>().SayHello();

        locator.GetService<ServiceA>().SayHello();
    }
}

Program input as shown below:

We can be registered by way of service to dynamically create an implicit structure according to their corresponding constructor parameter types, and this DotNetCorein ServiceProvider manner very similar, it does not depend on the order of registration of our services, can be normal structure.

Official achieve

Above we achieve through their own hand a ServiceLocator generally understand the realization of the idea of them, it is necessary to look at how the official is implemented.

First, in use, so we generally use the following sample code:

var services= new ServiceCollection();

......
services.AddSingleton(Configuration.GetSection(nameof(AppSettings)).Get<AppSettings>());
......

ServiceProvider serviceProvider = services.BuildServiceProvider();

You can see, we are finally through a ServiceProvider to get our services provide an object, the class corresponding to the official source implementation as shown below:

By source we can see, all clients are registered into a IServiceProviderEngine object type, and the specific type of the object is based on ServiceProviderOptions to create ways. Here, there are two classes that we need to focus on note:

  • ServiceProvider
  • CallSiteFactory

The former is responsible for the upper injection, which is responsible for the ground up, so if you want to look at is how to instantiate official, then inject these objects can look at the corresponding implementation code.

to sum up

If you read all the code examples above me, looking back, in fact, not difficult at all, if he wrote it, you can also write out. But in practice, people can learn to live with, or rarely, in the final analysis is the question of the way of thinking. The official also achieved by reflection, but he will be more rigorous internal logic of some, which led to his realization will be more complicated, which is in the course of things.

Digression

CLICK ME (glass heart Please do not click)

I had not too much to say about this matter, but it is necessary to think about or talk about.

  • First of all, my people Zuifan senior black and small pink . In those years the beginning, I naively thought that technology circles and other industries compared to relatively better, because this circle of friends are engaged in technology. After a few years may, indeed my idea was beaten face. So now I see some things, meet people, how much I feel very sad. To tell the truth, the past few years, I've met a lot of pseudo-programmers , most of them prefer ism , never their own initiative to study the issue . See what others do, and that they followed what do encounter will not, or can not handle the rejection pot or let go. If he comes to ask you, you say the solution ideas to him, then let him take the time to research studies, he was unhappy, I will give you the label . I am trying to say is, when you start working out of school into the community, in addition to your parents, no one has the responsibility and obligation to give you a free specialized guidance, learning is its own thing, everything depends on human effort. Worth mentioning, I do not bother to explain, you are happy.
  • Secondly, I personally feel that copied the code this thing is not a shame. Their own lack of experience, look good code before veteran, used to learn, understand and appreciate the advantages and disadvantages of this design predecessors, and put it into their own. When later encounter some business scenarios can learn and use it, I have no objection to this behavior, and I personally have always been like this. But one thing I can not accept, but does not indicate the source of the plagiarism behavior, as long as your article inspiration that come from other places, regardless of quality, be sure to introduce the relevant source, otherwise it makes no difference and plagiarism. I really do not know that I was to see the title and content of an article in the blog Home Park, what to say. I will not point out a link to the article, but also look look out for themselves. In short, we hope every technical people do not write blog this thing play sour.

mutual encouragement!

Related reference

Guess you like

Origin www.cnblogs.com/hippieZhou/p/11401267.html