前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core 中的 ServiceProvider

ASP.NET Core 中的 ServiceProvider

作者头像
Edison.Ma
发布2019-08-29 09:51:35
1.9K0
发布2019-08-29 09:51:35
举报
文章被收录于专栏:DotNet Core圈圈

前言#

在 ASP.NET Core 中,微软提供了一套默认的依赖注入实现,该实现对应的包为:Microsoft.Extensions.DependencyInjection,我们可以通过查看其对应的开源仓库看一下它的具体实现。基于该实现,我们不必显式创建我们的服务对象,可以将其统一注入到 ServiceProvider 中进行集中维护,使用的时候直接在该对象中获取即可。让我们在编写业务逻辑时,不用太关注对象的创建和销毁。这也是为什么现在有些最佳实践中建议不要过多使用 New 的方式来获取对象。在本文中,我们将一起了解一下如何实现一个自己的 ServiceProvider。

自己动手,丰衣足食#

为了方便区分,我这里自定义定义的类叫:ServiceLocator,其功能与官方的 ServiceProvider 类似。

基本实现#

首先,我们需要定义一个简单的服务发现接口,用于约束上层具体的实现,示例代码如下所示:

代码语言:javascript
复制
Copy
public interface IServiceLocator
{
    void AddService<T>();
    T GetService<T>();
}

接着,我们定义一个继承上述接口的具体实现类,示例代码如下所示:

代码语言:javascript
复制
Copy
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");
        }
    }
}

这样,我们就实现了一个最基本的服务发现类,通过该类,我们可以将我们的多个服务进行集中管理。这里我为了方便,模拟了 3 个服务类用于注册,示例代码如下所示:

代码语言:javascript
复制
Copy
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");
    }
}

使用方式就很简单了,如下所示:

代码语言:javascript
复制
Copy
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();
    }
}

程序运行效果如下图所示:

程序看起来运行不错,结果也符合我们的预期。但是稍微有点工作经验的朋友就会发现上述的实现是有很多潜在问题的。对于 IServiceLocator 的实例,我们一般会以单例模式来进行使用,这就会设计到线程安全的委托,所以我们的服务列表必须要是线程安全的。此外,如果我们需要注册的服务过多,通过上述方式来进行注册的话会加到系统开销,因为我们的服务一旦注册进去就会立刻被初始化,从而耗费不必要的系统内存,所以我们应该让其实例化推迟,在使用的时候才进行实例化操作。下面我们对上述问题一一进行改进。

单例模式#

单例模式是一种最简单也是使用最频繁的设计模式,单例模式本身也有很多形式,感兴趣的可以查看我之前的博文:设计模式系列 - 单例模式,这里,我采用 线程安全 方式来修改我们的 ServiceLocator,此外,我们还需要将我们的服务集合类修改为线程安全类型。所以,整个修改完毕后,示例代码如下所示:

代码语言:javascript
复制
Copy
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");
        }
    }
}

延迟加载#

要想让所有的注册的服务支持懒加载,我们需要引入一个新的集合,这个新的集合是用于存储我们相应的实例对象,在注册的时候我们只记录注册类型,在需要访问到相应的服务时,我们只需要在这个实例集合列表中访问,如果发现我们需要的服务还未被实例化,那我们再进行实例化,然后将该实例化对象存储起来并返回。对于用哪种数据结构来存,我们可以采用多种数据结构,我这里仍然采用字典来存储,示例代码如下所示:

代码语言:javascript
复制
Copy
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)];
    }
}

自匹配构造#

上面的所有改进都支持无参构造函数的服务,但是对于有参构造函数的服务注册,我们定义的 服务提供者就不满足的,因为上述的反射方式是不支持有参构造函数的。对于这种情况我们有两种解决办法。第一种是将服务的初始化放到最上层,然后 ServiceLocator 通过一个 Fun 的方式来获取该示例,并存储起来,我们称之为 显示创建。第二种方式依然是通过反射方式,只是这个反射可能会复杂一下,我们称之为 隐式创建。我们分别对于这两个实现方式进行代码示例。

  • 显示构造
代码语言:javascript
复制
Copy
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();
    }
}

程序输出如下图所示:

当我们需要注册的服务对应的有参构造函数中的参数不需要注册到 ServiceLocator,那我们可以采用这种方法进行服务注册,比较灵活。

  • 隐式构造
代码语言:javascript
复制
Copy
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();
    }
}

程序输入如下图所示:

通过隐式构造的方式可以将我们待注册的服务依据其对应的构造函数参数类型来动态创建,这和 DotNetCore 中的 ServiceProvider 的方式很相似,它不依赖于我们服务的注册顺序,都能正常的进行构造。

官方实现#

上面我们通过自己手动实现了一个 ServiceLocator 大致明白了其中的实现思路,所以有必要看一下官方是如何实现的。

首先,在使用方式上,我们一般这么使用,示例代码如下所示:

代码语言:javascript
复制
Copy
var services= new ServiceCollection();

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

ServiceProvider serviceProvider = services.BuildServiceProvider();

可以看到,最终我们是通过一个 ServiceProvider 来获取我们的服务提供对象,该类对应官方的源码实现如下图所示:

通过源码我们不难看出,所有的服务对象都是注册进了一个 IServiceProviderEngine 类型的对象,而该对象的具体类型又是根据 ServiceProviderOptions 的方式来进行创建。这里,有两个类我们需要着重注意一下:

  • ServiceProvider
  • CallSiteFactory

前者负责上层注入,后者负责底层构建,所以如果你想看一下官方是如何实例化这些注入对象的话可以看一下对应的实现代码。

总结#

如果你看完了我上面所有的代码示例,回头想想,其实一点都不难,要是自己写的话,也是可以写出来的。但是在实际工作中,能够活学或用的人却很少,归根到底就是思维方式的问题。官方也是通过反射来实现的,只不过他的内部逻辑会更严谨一些,这就导致了他的实现会更复杂一些,这也是里所当然的事情。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet技术平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言#
  • 自己动手,丰衣足食#
    • 基本实现#
      • 单例模式#
        • 延迟加载#
          • 自匹配构造#
          • 官方实现#
          • 总结#
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档