第48章 IServiceProvider、IUrlHelper、HttpClient深入理解

IServiceProvider

    IServiceProvider是.Net(Core)框架内置的,它作用是以反射方式,把已经注入到.Net(Core)框架中的指定接口/类进行实例化操作,相对于拷贝构造方法以反射方式,把已经注入到.Net(Core)框架中的指定接口/类进行实例化操作而言,使开发者可以以更加灵活的方式获取指定接口/类实例;另一个因素是如果指定类在定义其拷贝构造方法时不必为其定义相应的参数以供为获取指定接口/类实例,则是可以通过调用IServiceProvider的实例来实例化指定接口/类实例。即IServiceProvider以灵活和不依赖于拷贝构造方法的方式,来获取已经注入到.Net(Core)框架中的指定接口/类的实例。

通过.Net(Core)内置管道(IApplicationBuilder.ApplicationServices)获取IServiceProvider实例

在IServiceProvider对其它的接口/类实例化之前,必须先获取IServiceProvider接口的实例,或对IServiceProvide接口进行实例化操作,IServiceProvider接口的实例化是通过.Net(Core)配置接口IApplicationBuilder进行实例化的,因为IapplicationBuilder会实例化,多个基于IServiceProvider接口的具体实现类的实例。

///

    /// 【服务提供程序初始化--类】

    ///

    /// 摘要:

    ///     通过.NetCore框架内置管道接口(IApplicationBuilder)实例,来实例化服务提供程序接口(IServiceProvider )实例,从而为获取程序中其它接口或类的实例提供支撑。

    /// 注意:

    ///     该类及其所有成员都被限定为静态,即该类及其成员在程序执行前已经被实例化(不需要通过显式的实例化关键字“new”,来重复的对该类及其成员的进行实例操作)。

    ///

    ///

    public static class ServiceProviderInit

    {

        #region 属性

        ///

        /// 【服务提供程序】

        ///

        /// 摘要:

        ///    获取/设置服务提供程序接口(IServiceProvider )实例。

        ///

        ///

        public static IServiceProvider ServiceProvider { get; set; }

        #endregion

        #region 方法

        /// name="application" >.NetCore框架内置管道接口实例。

        ///

        /// 【通过应用配置获取ServiceProvider】

        ///

        /// 摘要:

        ///    通过.NetCore框架内置管道接口(IApplicationBuilder)实例,来实例化服务提供程序接口(IServiceProvider)实例。

        ///

        ///

        public static void ServiceProviderByApplication(this IApplicationBuilder application)

        {

            ServiceProvider = application.ApplicationServices;

        }

        #endregion

    }

   

通过HttpContext.RequestServices/子容器接口(IServiceScope)获取IServiceProvider实例

///

    /// 【通过HttpContextAccessor初始化服务提供程序--类】

    ///

    /// 摘要:

    ///     通过HttpContext.RequestServices/IApplicationBuilder.ApplicationServices/子容器接口(IServiceScope)实例,来实例化服务提供程序接口(IServiceProvider)实例,从而为获取程序中其它接口或类的实例提供支撑。

    ///

    ///

    public class ServiceProviderByHttpContextAccessor

    {

        #region 方法--私有保护

        /// name="scope">子容器接口(IServiceScope)实例,该接口明确表示,会调用IDisposable.Disposable()方法来结束掉当前作用域的生命周期,默认值:null,即无新建的子容器接口(IServiceScope)实例。

        ///

        /// 【通过HTTP上下访问获取服务提供程序】

        ///

        /// 摘要:

        ///    如果无新建的子容器接口(IServiceScope)实例,优先通过HttpContext实例来实例化IServiceProvider实例;如果HttpContext实例为空,其次选择通过IApplicationBuilder.ApplicationServices来实例化IServiceProvider实例;

        ///    如果有新建的子容器接口(IServiceScope)实例,则通过新建的子容器接口(IServiceScope)对实例来实例化IServiceProvider实例,即获取该子容器的IServiceProvider实例。

        ///

        ///

        protected static IServiceProvider GetServiceProviderByHttpContextAccessor(IServiceScope scope = null)

        {

            //如果无子容器接口(IServiceScope)实例,优先通过HttpContext实例来实例化IServiceProvider实例;如果HttpContext实例为空,其次选择通过IApplicationBuilder.ApplicationServices来实例化IServiceProvider实例。

            if (scope == null)

            {

                //获取IHttpContextAccessor接口实例。

                var accessor = ServiceProviderInit.ServiceProvider?.GetService();

                //获取HttpContext实例。

                var context = accessor?.HttpContext;

                //如果HttpContext实例不为空,获取HttpContext实例的IServiceProvider实例;如果HttpContext实例为空,则通过IApplicationBuilder.ApplicationServices来实例化IServiceProvider实例。

                return context?.RequestServices ?? ServiceProviderInit.ServiceProvider;

            }

            //如果有新建的子容器接口(IServiceScope)实例,通过新建的子容器接口(IServiceScope)实例来实例化IServiceProvider实例,即获取该子容器的IServiceProvider实例。

            return scope.ServiceProvider;

        }

        #endregion

        #region 方法

        /// name="type">1个指定的泛型类的类型实例。

        /// name="scope">子容器接口(IServiceScope)实例,该接口明确表示,会调用IDisposable.Disposable()方法来结束掉当前作用域的生命周期,默认值:null,即无新建的子容器接口(IServiceScope)实例。

        ///

        /// 【解析】

        ///

        /// 摘要:

        ///    通过HttpContext.RequestServices/IApplicationBuilder.ApplicationServices/子容器接口(IServiceScope)实例,来实例化服务提供程序接口(IServiceProvider)实例,以反射方式获取已经注入到依赖注入容器中的1个指定接口/类的实例。

        ///

        ///

        /// 返回:

        ///    以反射方式获取已经注入到依赖注入容器中的1个指定接口/类的实例。

        ///

        ///

        public static object Resolve(Type type, IServiceScope scope = null)

        {

            return GetServiceProviderByHttpContextAccessor(scope)?.GetService(type);

        }

        /// name="T">泛型类型实例(这里主要指:1个指定类的类型实例)。

        /// name="scope">子容器接口(IServiceScope)实例,该接口明确表示,会调用IDisposable.Disposable()方法来结束掉当前作用域的生命周期,默认值:null,即子容器接口(IServiceScope)实例。

        ///

        /// 【解析】

        ///

        /// 摘要:

        ///    通过HttpContext.RequestServices/IApplicationBuilder.ApplicationServices/子容器接口(IServiceScope)实例,来实例化服务提供程序接口(IServiceProvider)实例,以反射方式获取已经注入到依赖注入容器中的1个指定接口/类的实例。

        ///

        ///

        /// 返回:

        ///    以反射方式获取已经注入到依赖注入容器中的1个指定接口/类的实例。

        ///

        ///

        public static T Resolve<T>(IServiceScope scope = null) where T : class

        {

            return (T)Resolve(typeof(T), scope);

        }

        #endregion

   

IServiceProvider接口实例实例化已经注入到容器的接口/类

IServiceProvider实例,在通过反射方式获取基于AddSingleton方法注入的接口/类的实例时,这些接口/类的实例,是全局的共享的,即这些接口/或类的实例及其生命周期是由“Root IServiceProvider”负责管理的,所以不通过基于IServiceProvider接口的那种具体实现类的实例来获取接口/类的实例,都会获取这些接口/类的同1个实例。

    IServiceProvider实例,在通过反射方式获取基于AddTransient方法注入的接口/类的实例时,这些接口/类的实例是瞬时的,即这些接口/或类的实例及其生命周期是由具体的方法或方法中的语句块负责管理的,当方法或方法中的语句决结果时这些接口/类的同1个实例,也会被销毁。

IServiceProvider实例,在通过反射方式获取基于AddScoped方法注入的接口/类的实例时,必须为其指定1个指定的“作用域”这些接口/类的实例及其生命周期是由“作用域”负责管理的;即这些实例不是全局的只针对于特定的作用域,如果不其特别指定1个指定的应用域,而直接通过application.ApplicationServices直接获取基于AddScoped方法注入的接口/类的实例就会出现异常:“System.InvalidOperationException:“Cannot resolve scoped service 'xxx.IScopedService' from root provider.””。在实际开发中是通过1个指定页面的1次指定请求的生命周期作为1个指定的“作用域”来管理基于AddScoped方法注入的接口/类的生命周期,即接口/类的生命周期=1个指定页面的1次指定请求的生命周期。通常的定义方式是:

ServiceProvider?.GetService()?.HttpContext.RequestServices.GetService();

实际上通过拷贝构造方法所获取的IServiceProvide接口实例,本质上也是通过1个指定的HttpContext实例的生命周期,作为基于AddScoped方法注入的接口/类的实例的生命周期。例如:

private readonly IServiceProvider _serviceProviderConstructor;

public HomeController(IServiceProvider serviceProviderConstructor, IServiceScopeFactory serviceScopeFactory)

        {

            _serviceProviderConstructor = serviceProviderConstructor;

            ServiceScopeFactory = serviceScopeFactory;

            _serviceScopeFactory = serviceScopeFactory;

        }

_serviceProviderConstructor= ServiceProvider?.GetService()?.HttpContext.RequestServices
    HttpContext.RequestServices会获取基于AddSingleton、AddTransient接口/类的实例,但对生命周期的管理只针对AddScoped,AddSingleton的销毁是由最高管理层“Root IServiceProvider”负责,而AddTransient则是在执行HttpContext.RequestServices操作的过程中,已经把这些接口/类的同1个实例销毁掉了。

 第48章 IServiceProvider、IUrlHelper、HttpClient深入理解_第1张图片

//异常示例:会显示逻辑异常信息:"System.InvalidOperationException:“Cannot resolve scoped service 'Web.Services.IScopedService' from root provider.”"。

            //DataInfoDictionary.Add($"_scopedService_____GetService_____Exception", ServiceProviderInit.ServiceProvider.GetService().GetDataInfo("1"));

            //通过显式新建的1个子容器,来获取基于AddScoped方法注入的接口/类的实例,从而解决上述异常。

            DataInfoDictionary.Add($"_scopedService_____CreateScope", ServiceProviderInit.ServiceProvider.CreateScope().ServiceProvider.GetService().GetDataInfo("1"));

            //通过示例可以得出两个子容器是不同的,子容器与HttpContext.RequestServices也是不同的。

            DataInfoDictionary.Add($"_scopedService_____CreateScope_____1", ServiceProviderInit.ServiceProvider.CreateScope().ServiceProvider.GetService().GetDataInfo("1"));

            //通过1个指定的HttpContext实例,来管理通过AddScoped方法注入所接口/类实例的生命周期,从而解决上述异常。

            DataInfoDictionary.Add($"_scopedService_____HttpContextAccessor", ServiceProviderByHttpContextAccessor.Resolve().GetDataInfo("1"));

            DataInfoDictionary.Add($"_scopedService_____HttpContextAccessor_____1", ServiceProviderByHttpContextAccessor.Resolve().GetDataInfo("1"));

            DataInfoDictionary.Add($"_scopedService_____HttpContextAccessor_____ServiceScopeFactory_____CreateScope", ServiceProviderByHttpContextAccessor.Resolve(ServiceScopeFactory.CreateScope()).GetDataInfo("1"));

            DataInfoDictionary.Add($"_scopedService_____HttpContextAccessor_____serviceScopeFactory____CreateScope", ServiceProviderByHttpContextAccessor.Resolve(_serviceScopeFactory.CreateScope()).GetDataInfo("1"));

            //通过拷贝构造方法,以反射方式获取指定接口/类的作用域实例,可以直接获取指定接口/类的作用域实例,本质上依然是:通过1个指定的HttpContext实例,来管理通过AddScoped方法注入所接口/类实例的生命周期。

            DataInfoDictionary.Add($"_scopedService_____serviceProviderConstructor", _serviceProviderConstructor.GetService().GetDataInfo("1"));

            DataInfoDictionary.Add($"_scopedService_____serviceProviderConstructor_____1", _serviceProviderConstructor.GetService().GetDataInfo("1"));

IUrlHelper

    IurlHelper是在MVC模板的基础上进行定义,通过该接口中的成员获取程序中的URL包含路由、查询字符串等,一般常用的成员有:

  1. ViewBag.UrlActionServiceProvider = Url.Action("ServiceProvider");
  2. ViewBag.ActionContext = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);

HttpClient

    HttpClient是由.Net(Core)框架内置的,用于把第3方网站所提供的数据集成到当前程序中,其中的主要应用有通过第3方网站提供的身份认证机制,执行当前程序的登录操作,关于HttpClient更多的应用见:“https://zetcode.com/csharp/httpclient/”。

   

通过new关键字实例化HttpClient

public async Task HttpClientByNew()

        {

            var userName = "user7";

            var passwd = "passwd";

            var url = "https://httpbin.org/basic-auth/user7/passwd";

            using var client = new HttpClient();

            //设置请求和传输超时时间:30秒,如果30秒后未收到回复,抛出异常:return Empty

            client.Timeout = TimeSpan.FromSeconds(30);

            var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",

                    Convert.ToBase64String(authToken));

            var result = await client.GetAsync(url);

            var content = await result.Content.ReadAsStringAsync();

            return Content(content);

        }

通过HttpClient或IHttpClientFactory实例化HttpClient

通过HttpClient或IHttpClientFactory实例化HttpClient,必须首先在内置依赖注入容器中注入:builder.Services.AddHttpClient();,否则会出现逻辑异常:“InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'Web.Controllers.HomeController'.”

  private readonly HttpClient _httpClient;

private readonly IHttpClientFactory _httpClientFactory;

  public HomeController(IServiceProvider serviceProviderConstructor,

            IServiceScopeFactory serviceScopeFactory,

            IUrlHelperFactory urlHelperFactory,

            IActionContextAccessor actionContextAccessor,

            HttpClient httpClient,

            IHttpClientFactory httpClientFactory

            )

        {

            _serviceProviderConstructor = serviceProviderConstructor;

            ServiceScopeFactory = serviceScopeFactory;

            _serviceScopeFactory = serviceScopeFactory;

            _urlHelperFactory = urlHelperFactory;

            _actionContextAccessor = actionContextAccessor;

            //设置请求和传输超时时间:30秒,如果30秒后未收到回复,抛出异常:return Empty

            httpClient.Timeout = TimeSpan.FromSeconds(30);

            _httpClient = httpClient;

            _httpClientFactory = httpClientFactory;

        }

public async Task HttpClientByHttpClient()

        {

            var userName = "user7";

            var passwd = "passwd";

            var url = "https://httpbin.org/basic-auth/user7/passwd";

            var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");

            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",

                    Convert.ToBase64String(authToken));

            var result = await _httpClient.GetAsync(url);

            var content = await result.Content.ReadAsStringAsync();

            return Content(content);

        }

        public async Task HttpClientByHttpClientFactory()

        {

            var userName = "user7";

            var passwd = "passwd";

            var url = "https://httpbin.org/basic-auth/user7/passwd";

            using var client = _httpClientFactory.CreateClient();

            //设置请求和传输超时时间:30秒,如果30秒后未收到回复,抛出异常:return Empty

            client.Timeout = TimeSpan.FromSeconds(30);

            var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",

                    Convert.ToBase64String(authToken));

            var result = await client.GetAsync(url);

            var content = await result.Content.ReadAsStringAsync();

            return Content(content);

        }

对以上功能更为具体实现和注释见:22-05-11-048_IServiceProvider_IUrlHelper_HttpClient(深入理解)。

你可能感兴趣的:(.netCore,框架)