当前位置: 首页 > news >正文

cms网站制作开源免费企业网站系统

cms网站制作,开源免费企业网站系统,免费注册163,互联网技术学校前言看到标题可能大家会有所疑问Controller和IOC能有啥羁绊#xff0c;但是我还是拒绝当一个标题党的。相信有很大一部分人已经知道了这么一个结论#xff0c;默认情况下ASP.NET Core的Controller并不会托管到IOC容器中#xff0c;注意关键字我说的是默认#… 前言    看到标题可能大家会有所疑问Controller和IOC能有啥羁绊但是我还是拒绝当一个标题党的。相信有很大一部分人已经知道了这么一个结论默认情况下ASP.NET Core的Controller并不会托管到IOC容器中注意关键字我说的是默认首先咱们不先说为什么如果还有不知道这个结论的同学们可以自己验证一下验证方式也很简单大概可以通过以下几种方式。验证Controller不在IOC中首先我们可以尝试在ServiceProvider中获取某个Controller实例比如public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {var productController app.ApplicationServices.GetServiceProductController(); } 这是最直接的方式可以在IOC容器中获取注册过的类型实例很显然结果会为null。另一种方式也是利用它的另一个特征那就是通过构造注入的方式如下所示我们在OrderController中注入ProductController,显然这种方式是不合理的但是为了求证一个结果我们这里仅做演示强烈不建议实际开发中这么写这是不规范也是不合理的写法public class OrderController : Controller {private readonly ProductController _productController;public OrderController(ProductController productController){_productController productController;}public IActionResult Index(){return View();} } 结果显然是会报一个错InvalidOperationException: Unable to resolve service for type ProductController while attempting to activate OrderController。原因就是因为ProductController并不在IOC容器中,所以通过注入的方式会报错。还有一种方式可能不太常用这个是利用注入的一个特征可能有些同学已经了解过了那就是通过自带的DI即使一个类中包含多个构造函数它也会选择最优的一个也就是说自带的DI允许类包含多个构造函数。利用这个特征我们可以在Controller中验证一下public class OrderController : Controller {private readonly IOrderService _orderService;private readonly IPersonService _personService;public OrderController(IOrderService orderService){_orderService orderService;}public OrderController(IOrderService orderService, IPersonService personService){_orderService orderService;_personService personService;}public IActionResult Index(){return View();} } 我们在Controller中编写了两个构造函数理论上来说这是符合DI特征的运行起来测试一下依然会报错InvalidOperationException: Multiple constructors accepting all given argument types have been found in type OrderController. There should only be one applicable constructor。以上种种都是为了证实一个结论默认情况下Controller并不会托管到IOC当中。DefaultControllerFactory源码探究    上面虽然我们看到了一些现象能说明Controller默认情况下并不在IOC中托管但是还没有足够的说服力接下来我们就来查看源码,这是最有说服力的。我们找到Controller工厂注册的地方在MvcCoreServiceCollectionExtensions扩展类中[点击查看源码????]的AddMvcCoreServices方法里//给IControllerFactory注册默认的Controller工厂类DefaultControllerFactory //也是Controller创建的入口 services.TryAddSingletonIControllerFactory, DefaultControllerFactory(); //真正创建Controller的工作类DefaultControllerActivator services.TryAddTransientIControllerActivator, DefaultControllerActivator(); 由此我们可以得出默认的Controller创建工厂类为DefaultControllerFactory那么我们直接找到源码位置[点击查看源码????],为了方便阅读精简一下源码如下所示internal class DefaultControllerFactory : IControllerFactory {//真正创建Controller的工作者private readonly IControllerActivator _controllerActivator;private readonly IControllerPropertyActivator[] _propertyActivators;public DefaultControllerFactory(IControllerActivator controllerActivator,IEnumerableIControllerPropertyActivator propertyActivators){_controllerActivator controllerActivator;_propertyActivators propertyActivators.ToArray();}/// summary/// 创建Controller实例的方法/// /summarypublic object CreateController(ControllerContext context){//创建Controller实例的具体方法(这是关键方法)var controller _controllerActivator.Create(context);foreach (var propertyActivator in _propertyActivators){propertyActivator.Activate(context, controller);}return controller;}/// summary/// 释放Controller实例的方法/// /summarypublic void ReleaseController(ControllerContext context, object controller){_controllerActivator.Release(context, controller);} } 用过上面的源码可知真正创建Controller的地方在_controllerActivator.Create方法中通过上面的源码可知为IControllerActivator默认注册的是DefaultControllerActivator类直接找到源码位置[点击查看源码????],我们继续简化一下源码如下所示internal class DefaultControllerActivator : IControllerActivator {private readonly ITypeActivatorCache _typeActivatorCache;public DefaultControllerActivator(ITypeActivatorCache typeActivatorCache){_typeActivatorCache typeActivatorCache;}/// summary/// Controller实例的创建方法/// /summarypublic object Create(ControllerContext controllerContext){//获取Controller类型信息var controllerTypeInfo controllerContext.ActionDescriptor.ControllerTypeInfo;//获取ServiceProvidervar serviceProvider controllerContext.HttpContext.RequestServices;//创建controller实例return _typeActivatorCache.CreateInstanceobject(serviceProvider, controllerTypeInfo.AsType());}/// summary/// 释放Controller实例/// /summarypublic void Release(ControllerContext context, object controller){//如果controller实现了IDisposable接口那么Release的时候会自动调用Controller的Dispose方法//如果我们在Controller中存在需要释放或者关闭的操作可以再Controller的Dispose方法中统一释放if (controller is IDisposable disposable){disposable.Dispose();}} } 通过上面的代码我们依然要继续深入到ITypeActivatorCache实现中去寻找答案通过查看MvcCoreServiceCollectionExtensions类的AddMvcCoreServices方法源码我们可以找到如下信息services.TryAddSingletonITypeActivatorCache, TypeActivatorCache(); 有了这个信息我们可以直接找到TypeActivatorCache类的源码[点击查看源码????]代码并不多大致如下所示internal class TypeActivatorCache : ITypeActivatorCache {//创建ObjectFactory的委托private readonly FuncType, ObjectFactory _createFactory (type) ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);//Controller类型和对应创建Controller实例的ObjectFactory实例的缓存private readonly ConcurrentDictionaryType, ObjectFactory _typeActivatorCache new ConcurrentDictionaryType, ObjectFactory();/// summary/// 真正创建实例的地方/// /summarypublic TInstance CreateInstanceTInstance(IServiceProvider serviceProvider,Type implementationType){//真正创建的操作是createFactory//通过Controller类型在ConcurrentDictionary缓存中获得ObjectFactory//而ObjectFactory实例由ActivatorUtilities.CreateFactory方法创建的var createFactory _typeActivatorCache.GetOrAdd(implementationType, _createFactory);//返回创建实例return (TInstance)createFactory(serviceProvider, arguments: null);} } 通过上面类的代码我们可以清晰的得出一个结论默认情况下Controller实例是由ObjectFactory创建出来的而ObjectFactory实例是由ActivatorUtilities的CreateFactory创建出来,所以Controller实例每次都是由ObjectFactory创建而来并非注册到IOC容器中。并且我们还可以得到一个结论ObjectFactory应该是一个委托我们找到ObjectFactory定义的地方[点击查看源码????]delegate object ObjectFactory(IServiceProvider serviceProvider, object[] arguments); 这个确实如我们猜想的那般这个委托会通过IServiceProvider实例去构建类型的实例通过上述源码相关的描述我们会产生一个疑问既然Controller实例并非由IOC容器托管它由ObjectFactory创建而来但是ObjectFactory实例又是由ActivatorUtilities构建的那么生产对象的核心也就在ActivatorUtilities类中接下来我们就来探究一下ActivatorUtilities的神秘面纱。ActivatorUtilities类的探究    书接上面我们知道了ActivatorUtilities类是创建Controller实例最底层的地方那么ActivatorUtilities到底和容器是啥关系因为我们看到了ActivatorUtilities创建实例需要依赖ServiceProvider,一切都要从找到ActivatorUtilities类的源码开始。我们最初接触这个类的地方在于它通过CreateFactory方法创建了ObjectFactory实例那么我们就从这个地方开始找到源码位置[点击查看源码????]实现如下public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes) {//查找instanceType的构造函数//找到构造信息ConstructorInfo//得到给定类型与查找类型instanceType构造函数的映射关系FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);//构建IServiceProvider类型参数var provider Expression.Parameter(typeof(IServiceProvider), provider);//构建给定类型参数数组参数var argumentArray Expression.Parameter(typeof(object[]), argumentArray);//通过构造信息、构造参数对应关系、容器和给定类型构建表达式树Bodyvar factoryExpressionBody BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);//构建lambdavar factoryLamda Expression.LambdaFuncIServiceProvider, object[], object(factoryExpressionBody, provider, argumentArray);var result factoryLamda.Compile();//返回执行结果return result.Invoke; } ActivatorUtilities类的CreateFactory方法代码虽然比较简单但是它涉及到调用了其他方法由于嵌套的比较深代码比较多而且不是本文讲述的重点我们就不再这里细说了我们可以大概的描述一下它的工作流程。首先在给定的类型里查找到合适的构造函数这里我们可以理解为查找Controller的构造函数。然后得到构造信息并得到构造函数的参数与给定类型参数的对应关系通过构造信息和构造参数的对应关系在IServiceProvider得到对应类型的实例为构造函数赋值最后经过上面的操作通过初始化指定的构造函数来创建给定Controller类型的实例综上述的相关步骤我们可以得到一个结论Controller实例的初始化是通过遍历Controller类型构造函数里的参数然后根据构造函数每个参数的类型在IServiceProvider查找已经注册到容器中相关的类型实例最终初始化得到的Controller实例。这就是在IServiceProvider得到需要的依赖关系然后创建自己的实例它内部是使用的表达式树来完成的这一切可以理解为更高效的反射方式。关于ActivatorUtilities类还包含了其他比较实用的方法比如CreateInstance方法public static T CreateInstanceT(IServiceProvider provider, params object[] parameters) 它可以通过构造注入的方式创建指定类型T的实例其中构造函数里具体的参数实例是通过在IServiceProvider实例里获取到的比如我们我们有这么一个类public class OrderController {private readonly IOrderService _orderService;private readonly IPersonService _personService;public OrderController(IOrderService orderService, IPersonService personService){_orderService orderService;_personService personService;} } 其中它所依赖的IOrderService和IPersonService实例是注册到IOC容器中的IServiceCollection services new ServiceCollection().AddScopedIPersonService, PersonService().AddScopedIOrderService, OrderService(); 然后你想获取到OrderController的实例但是它只包含一个有参构造函数但是构造函数的参数都以注册到IOC容器中。当存在这种场景你便可以通过以下方式得到你想要的类型实例如下所示IServiceProvider serviceProvider services.BuildServiceProvider(); OrderController orderController  ActivatorUtilities.CreateInstanceOrderController(serviceProvider); 即使你的类型OrderController并没有注册到IOC容器中但是它的依赖都在容器中你也可以通过构造注入的方式得到你想要的实例。总的来说ActivatorUtilities里的方法还是比较实用的有兴趣的同学可以自行尝试一下也可以通过查看ActivatorUtilities源码的方式了解它的工作原理。AddControllersAsServices方法    上面我们主要是讲解了默认情况下Controller并不是托管到IOC容器中的它只是表现出来的让你以为它是在IOC容器中因为它可以通过构造函数注入相关实例这主要是ActivatorUtilities类的功劳。说了这么多Controller实例到底可不可以注册到IOC容器中让它成为真正受到IOC容器的托管者。要解决这个必须要满足两点条件首先需要将Controller注册到IOC容器中但是仅仅这样还不够因为Controller是由ControllerFactory创建而来其次我们要改造ControllerFactory类中创建Controller实例的地方让它从容器中获取Controller实例这样就解决了所有的问题如果我们自己去实现将Controller托管到IOC容器中就需要满足以上两个操作一个是要将Controller放入容器然后让创建Controller的地方从IOC容器中直接获取Controller实例。庆幸的是微软帮我们封装了一个相关的方法它可以帮我们解决将Controller托管到IOC容器的问题它的使用方法如下所示services.AddMvc().AddControllersAsServices(); //或其他方式这取决于你构建的Web项目的用途可以是WebApi、Mvc、RazorPage等 //services.AddMvcCore().AddControllersAsServices(); 相信大家都看到了玄机就在AddControllersAsServices方法中但是它存在于MvcCoreMvcBuilderExtensions类和MvcCoreMvcCoreBuilderExtensions类中不过问题不大因为它们的代码是完全一样的。只是因为你可以通过多种方式构建Web项目比如AddMvc或者AddMvcCore废话不多说直接上代码[点击查看源码????]public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder) {if (builder null){throw new ArgumentNullException(nameof(builder));}var feature new ControllerFeature();builder.PartManager.PopulateFeature(feature);//第一将Controller实例添加到IOC容器中foreach (var controller in feature.Controllers.Select(c c.AsType())){//注册的声明周期是Transientbuilder.Services.TryAddTransient(controller, controller);}//第二替换掉原本DefaultControllerActivator的为ServiceBasedControllerActivatorbuilder.Services.Replace(ServiceDescriptor.TransientIControllerActivator, ServiceBasedControllerActivator());return builder; } 第一点没问题那就是将Controller实例添加到IOC容器中第二点它替换掉了DefaultControllerActivator为为ServiceBasedControllerActivator。通过上面我们讲述的源码了解到DefaultControllerActivator是默认提供Controller实例的地方是获取Controller实例的核心所在那么我们看看ServiceBasedControllerActivator与DefaultControllerActivator到底有何不同直接贴出代码[点击查看源码????]public class ServiceBasedControllerActivator : IControllerActivator {public object Create(ControllerContext actionContext){if (actionContext null){throw new ArgumentNullException(nameof(actionContext));}//获取Controller类型var controllerType actionContext.ActionDescriptor.ControllerTypeInfo.AsType();//通过Controller类型在容器中获取实例return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);}public virtual void Release(ControllerContext context, object controller){} } 相信大家对上面的代码一目了然了和我们上面描述的一样将创建Controller实例的地方改造了在容器中获取的方式。不知道大家有没有注意到ServiceBasedControllerActivator的Release的方法居然没有实现这并不是我没有粘贴出来确实是没有代码之前我们看到的DefaultControllerActivator可是有调用Controller的Disposed的方法这里却啥也没有。相信聪明的你已经想到了因为Controller已经托管到了IOC容器中所以他的生命及其相关释放都是由IOC容器完成的所以这里不需要任何操作。    我们上面还看到了注册Controller实例的时候使用的是TryAddTransient方法也就是说每次都会创建Controller实例至于为什么我想大概是因为每次请求都其实只会需要一个Controller实例况且EFCore的注册方式官方建议也是Scope的而这里的Scope正是对应的一次Controller请求。在加上自带的IOC会提升依赖类型的声明周期如果将Controller注册为单例的话如果使用了EFCore那么它也会被提升为单例这样会存在很大的问题。也许正是基于这个原因默认才将Controller注册为Transient类型的当然这并不代表只能注册为Transient类型的如果你不使用类似EFCore这种需要作用域为Scope的服务的时候而且保证使用的主键都可以使用单例的话完全可以将Controller注册为别的生命周期当然这种方式个人不是很建议。Controller结合Autofac    有时候大家可能会结合Autofac一起使用Autofac确实是一款非常优秀的IOC框架它它支持属性和构造两种方式注入关于Autofac托管自带IOC的原理咱们在之前的文章浅谈.Net Core DependencyInjection源码探究中曾详细的讲解过这里咱们就不过多的描述了咱们今天要说的是Autofac和Controller的结合。如果你想保持和原有的IOC一致的使用习惯即只使用构造注入的话你只需要完成两步即可首先将默认的IOC容器替换为Autofac具体操作也非常简单如下所示public static IHostBuilder CreateHostBuilder(string[] args) Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder {webBuilder.UseStartupStartup();})//只需要在这里设置ServiceProviderFactory为AutofacServiceProviderFactory即可.UseServiceProviderFactory(new AutofacServiceProviderFactory()); 然后就是咱们之前说的要将Controller放入容器中然后修改生产Controller实例的ControllerFactory的操作为在容器中获取当然这一步微软已经为我们封装了便捷的方法services.AddMvc().AddControllersAsServices(); 只需要通过上面简单得两步既可以将Controller托管到Autofac容器中。但是我们说过了Autofac还支持属性注入但是默认的方式只支持构造注入的方式那么怎么让Controller支持属性注入呢我们还得从最根本的出发那就是解决Controller实例存和取的问题首先为了让Controller托管到Autofac中并且支持属性注入那么就只能使用Autofac的方式去注册Controller实例具体操作是在Startup类中添加ConfigureContainer方法然后注册Controller并声明支持属性注入public void ConfigureContainer(ContainerBuilder builder) {var controllerBaseType typeof(ControllerBase);//扫描Controller类builder.RegisterAssemblyTypes(typeof(Program).Assembly).Where(t controllerBaseType.IsAssignableFrom(t) t ! controllerBaseType)//属性注入.PropertiesAutowired(); } 其次是解决取的问题这里我们就不需要AddControllersAsServices方法了因为AddControllersAsServices解决了Controller实例在IOC中存和取的问题但是这里我们只需要解决Controller取得问题说只需要使用ServiceBasedControllerActivator即可具体操作是services.Replace(ServiceDescriptor.TransientIControllerActivator, ServiceBasedControllerActivator()); 仅需要在默认的状态下完成这两步既可以解决Controller托管到Autofac中并支持属性注入的问题这也是最合理的方式。当然如果你使用AddControllersAsServices可是可以实现相同的效果了只不过是没必要将容器重复的放入容器中了。总结    本文我们讲述了关于ASP.NET Core Controller与IOC结合的问题我觉得这是有必要让每个人都有所了解的知识点因为在日常的Web开发中Controller太常用了知道这个问题可能会让大家在开发中少走一点弯路接下来我们来总结一下本文大致讲解的内容首先说明了一个现象那就是默认情况下Controller并不在IOC容器中我们也通过几个示例验证了一下。其次讲解了默认情况下创造Controller实例真正的类ActivatorUtilities并大致讲解了ActivatorUtilities的用途。然后我们找到了将Controller托管到IOC容器中的办法AddControllersAsServices并探究了它的源码了解了它的工作方式。最后我们又演示了如何使用最合理的方式将Controller结合Autofac一起使用并且支持属性注入。本次讲解到这里就差不多了希望本来就知道的同学们能加深一点了解不知道的同学能够给你们提供一点帮助能够在日常开发中少走一点弯路。新的一年开始了本篇文章是我2021年的第一篇文章新的一年感谢大家的支持。????欢迎扫码关注我的公众号????
http://www.yutouwan.com/news/490978/

相关文章:

  • 我要啦免费统计怎么做网站wordpress怎么自己写代码
  • 一站式网站建设设计南宁市建筑规划设计集团有限公司
  • 科技类网站设计特点模板网站的域名是什么
  • wordpress中文网站优化wordpress 自己可见
  • 空间链接制作网站wordpress公共课
  • 石家庄做网站的公司查企业数据要去什么网站
  • 有做企业网站的吗wordpress清理过期文件夹
  • 招聘网站开发查询企业邮箱
  • 网站关键字设置宁波网站搜索排名
  • 东莞市建设公共交易中心网站网络网站推广选择乐云seo
  • 人才网站建站静态网站开发篇
  • 手机网站怎么做淘宝客长沙专业网站建设服务
  • 网站项目流程表当当网站建设优点
  • 界首网站优化公司asp网站上传
  • 网站开发的几个主要阶段网页传奇游戏排行榜2014前十名
  • 怎么用dw英文版做网站三亚发布紧急通知
  • 四川省广安建设局网站市场监督管理局是工商局吗
  • 湖北省两学一做网站wordpress 子分类文章
  • 网站建设合同封面免费的黄台直播
  • 做门户网站价格竞价托管 微竞价
  • 做网站大概需要几步php 网站建设
  • 信誉好的苏州网站建设wordpress 4.4.7
  • 网站做百度地图定位wordpress怎么发布公告
  • 免费网站你会回来感谢我的外贸公司网页设计
  • 平台类网站wordpress apk源码
  • 包头有没有专业做淘宝网站的洛阳做网站哪家便宜
  • 中国品牌网站建设闷声赚钱的10个副业
  • 简单的购物网站模板上海注册公司流程及费用
  • wordpress抓取别人网站海东市公司网站建设
  • 做网站用什么框架WordPress page filed