ASP.NET Core Controller与IOC的羁绊

前言

    看到标题可能大家会有所疑问Controller和IOC能有啥羁绊,但是我还是拒绝当一个标题党的。相信有很大一部分人已经知道了这么一个结论,默认情况下ASP.NET Core的Controller并不会托管到IOC容器中,注意关键字我说的是"默认",首先咱们不先说为什么,如果还有不知道这个结论的同学们可以自己验证一下,验证方式也很简单,大概可以通过以下几种方式。

验证Controller不在IOC中

首先,我们可以尝试在ServiceProvider中获取某个Controller实例,比如

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{var productController = app.ApplicationServices.GetService<ProductController>();
}

这是最直接的方式,可以在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.TryAddSingleton<IControllerFactory, DefaultControllerFactory>();
//真正创建Controller的工作类DefaultControllerActivator
services.TryAddTransient<IControllerActivator, DefaultControllerActivator>();

由此我们可以得出,默认的Controller创建工厂类为DefaultControllerFactory,那么我们直接找到源码位置[点击查看源码????],
为了方便阅读,精简一下源码如下所示

internal class DefaultControllerFactory : IControllerFactory
{//真正创建Controller的工作者private readonly IControllerActivator _controllerActivator;private readonly IControllerPropertyActivator[] _propertyActivators;public DefaultControllerFactory(IControllerActivator controllerActivator,IEnumerable<IControllerPropertyActivator> propertyActivators){_controllerActivator = controllerActivator;_propertyActivators = propertyActivators.ToArray();}/// <summary>/// 创建Controller实例的方法/// </summary>public object CreateController(ControllerContext context){//创建Controller实例的具体方法(这是关键方法)var controller = _controllerActivator.Create(context);foreach (var propertyActivator in _propertyActivators){propertyActivator.Activate(context, controller);}return controller;}/// <summary>/// 释放Controller实例的方法/// </summary>public 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实例的创建方法/// </summary>public object Create(ControllerContext controllerContext){//获取Controller类型信息var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;//获取ServiceProvidervar serviceProvider = controllerContext.HttpContext.RequestServices;//创建controller实例return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());}/// <summary>/// 释放Controller实例/// </summary>public 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.TryAddSingleton<ITypeActivatorCache, TypeActivatorCache>();

有了这个信息,我们可以直接找到TypeActivatorCache类的源码[点击查看源码????]代码并不多,大致如下所示

internal class TypeActivatorCache : ITypeActivatorCache
{//创建ObjectFactory的委托private readonly Func<Type, ObjectFactory> _createFactory =(type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);//Controller类型和对应创建Controller实例的ObjectFactory实例的缓存private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =new ConcurrentDictionary<Type, ObjectFactory>();/// <summary>/// 真正创建实例的地方/// </summary>public TInstance CreateInstance<TInstance>(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.Lambda<Func<IServiceProvider, 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 CreateInstance<T>(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().AddScoped<IPersonService, PersonService>().AddScoped<IOrderService, OrderService>();

然后你想获取到OrderController的实例,但是它只包含一个有参构造函数,但是构造函数的参数都以注册到IOC容器中。当存在这种场景你便可以通过以下方式得到你想要的类型实例,如下所示

IServiceProvider serviceProvider = services.BuildServiceProvider();
OrderController orderController = ActivatorUtilities.CreateInstance<OrderController>(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.Transient<IControllerActivator, 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.UseStartup<Startup>();})//只需要在这里设置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.Transient<IControllerActivator, ServiceBasedControllerActivator>());

仅需要在默认的状态下完成这两步,既可以解决Controller托管到Autofac中并支持属性注入的问题,这也是最合理的方式。当然如果你使用AddControllersAsServices可是可以实现相同的效果了,只不过是没必要将容器重复的放入容器中了。

总结

    本文我们讲述了关于ASP.NET Core Controller与IOC结合的问题,我觉得这是有必要让每个人都有所了解的知识点,因为在日常的Web开发中Controller太常用了,知道这个问题可能会让大家在开发中少走一点弯路,接下来我们来总结一下本文大致讲解的内容

  • 首先说明了一个现象,那就是默认情况下Controller并不在IOC容器中,我们也通过几个示例验证了一下。

  • 其次讲解了默认情况下创造Controller实例真正的类ActivatorUtilities,并大致讲解了ActivatorUtilities的用途。

  • 然后我们找到了将Controller托管到IOC容器中的办法AddControllersAsServices,并探究了它的源码,了解了它的工作方式。

  • 最后我们又演示了如何使用最合理的方式将Controller结合Autofac一起使用,并且支持属性注入。

本次讲解到这里就差不多了,希望本来就知道的同学们能加深一点了解,不知道的同学能够给你们提供一点帮助,能够在日常开发中少走一点弯路。新的一年开始了,本篇文章是我2021年的第一篇文章,新的一年感谢大家的支持。

????欢迎扫码关注我的公众号????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305961.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

引入Jaeger——扩展

Jaeger是收集全链路跟踪的信息&#xff0c;在Jaeger收集的信息中&#xff0c;有请求的url信息&#xff0c;有每个请求的时间间隔&#xff0c;借助这些信息可以进行报警&#xff0c;比如一次较长的请求&#xff0c;或者是某些请求的次数和先后等。不管报警的业务规则是什么&…

判断 服务器架构性能 数据,服务器架构之性能扩展-第五章(6)

Mysql>create table t2 like t1;//复制表结构Mysql>insert into t2 select * from t1; //复制数据内容索引是一种快速查询的有效方法&#xff0c;可以通过alter增加索引或create语句创建。mysql>alter table t1 add ind_id (id);mysql>alter table t1 add unique/p…

如何在 ASP.NET Core 中 使用 功能开关

.NET Core 中的 功能管理 (Feature Management) 包可用于实现 功能开关&#xff0c;什么意思呢&#xff1f;就是可以通过 功能开关 特性动态的改变应用程序的行为而不需要改变任何的业务逻辑代码&#xff0c;听起来是不是挺有意思&#xff0c;本篇我们就来讨论如何使用这个包。…

「译」 .NET 5 新增的Http, Sockets, DNS 和 TLS 遥测

.NET 一直在稳定的增加和改善对应用程序进行跨平台的诊断分析&#xff0c;在.NET Core 3.0&#xff0c; 我们看到了 EventCounters[1] 的介绍&#xff0c;用于观察和分析指标测量。我最近在几个 .NET Core 的应用程序中使用 counters&#xff0c;来跟踪服务一段时间内 http 的请…

服务器磁盘系统,服务器磁盘阵列与操作系统

服务器磁盘阵列与操作系统 内容精选换一换硬件要求如表1所示。操作系统要求如表2所示。上表中所需Ubuntu下载地址&#xff1a;服务器1&#xff1a;Ubuntu 18.04.1镜像包、Ubuntu 16.04.4 镜像包。服务器2&#xff1a;Ubuntu 16.04.3 镜像包。在安装操作系统过程中“选择软件列表…

Prism for WPF 搭建一个简单的模块化开发框架

站长今天发现一个好项目&#xff0c;看原文贴图挺不错的&#xff0c;好项目应该让更多人知道&#xff0c;特别是该项目使得Prism搭建&#xff0c;网上使用prism做的开源成熟项目不多&#xff0c;这个值得大家研究。看项目gitee仓库截图&#xff0c;项目不错&#xff0c;star太少…

探索 .Net Core 的 SourceLink

介绍Source Link是一组软件包和一个规范&#xff0c; 它将一些元数据添加到PDB文件&#xff0c;以将本地文件重新映射到GitHub上的文件&#xff0c;因此Visual Studio可以在这需要时下载文件, 该项目的目的是可以为使用Nuget安装软件的用户提供源代码调试, Microsoft库&#xf…

「译」 用 Blazor WebAssembly 实现微前端

原文作者: Wael Kdouh原文链接&#xff1a;https://medium.com/waelkdouh/microfrontends-with-blazor-webassembly-b25e4ba3f325我聊下最近我在做的事情&#xff0c;然后分享下在Blazor WebAssembly 微前端的实现细节&#xff0c;这篇文章是我的一些心得&#xff0c;以及一个示…

使用 dotnet-monitor 分析.NET 应用程序

dotnet-monitor 是 .NET Core 命令行接口 (CLI) 工具, 可以很方便的在dotnet环境中分析我们的应用程序&#xff0c;需要注意的是&#xff0c;目前它还只是一个实验性的工具在这之前&#xff0c;我们使用的有 dotnet-counters&#xff0c;dotnet-dump 和 dotnet-trace&#xff0…

mysql数据库查看用户名_mysql怎么查看用户名?

我们在使用mysql的时候&#xff0c;有时候需要来查看当前用户。本篇文章将和大家讲述mysql怎么查看用户名&#xff0c;感兴趣的朋友可以了解一下。启动数据库&#xff1a;[rootserver ~]# mysqld_safe &[1] 3289[rootserver ~]# 130913 08:19:58 mysqld_safe Logging to /u…

如何实现 ASP.NET Core WebApi 的版本化

Web API 的版本化可以尽量保证在相同url情况下保留一个 api 的多个版本&#xff0c;通常一个 webapi 会有多个client&#xff0c;这些client包括&#xff1a;app&#xff0c;web&#xff0c;html5&#xff0c;crawl 等等同构或者异构的平台&#xff0c;当 api 升级之后&#xf…

mysql查询重复的名字_Mysql中like用法:查询名字中含有风字的学生信息

一、表名和字段–1. 学生表 student (s_id,s_name,s_birth,s_sex) –学生编号&#xff0c;学生姓名&#xff0c;出生年月&#xff0c;学生性别–2. 课程表 course (c_id,c_name,t_id) –课程编号&#xff0c;课程名称&#xff0c;教师编号–3. 教师表 teacher (t_id,t_name) –…

一次Task.Run异常问题的排查

最近在测试一个功能代码时发现一个非常奇怪的问题&#xff0c;主要是Task.Run引起一些不符合逻辑的错误&#xff0c;以下针对这一问题排查的总结。问题代码可以建个控制台程序来运行以下代码class Program{static User user new User();static void Main(string[] args){for (…

git maven 一键部署_Jenkins Git Maven搭建自动化部署项目环境 邮件通知

简介折腾了两个晚上&#xff0c;趁着今晚比较有空&#xff0c;把jenkinsgitmaven搭建自动化部署项目环境搭建的过程记录一下,这里我把github作为git的远程仓库(https://github.com/jacky- lulu/cxf_demo-Maven-Webapp)系统&#xff1a;centos6.5maven: Apache Maven 3.3…

在.NET Core 中收集数据的几种方式

APM是一种应用性能监控工具&#xff0c;可以帮助理解系统行为, 用于分析性能问题的工具&#xff0c;以便发生故障的时候&#xff0c;能够快速定位和解决问题, 通过汇聚业务系统各处理环节的实时数据&#xff0c;分析业务系统各事务处理的交易路径和处理时间&#xff0c;实现对应…

java 连接池连接mysql数据库需要哪些jar包_DBCP-基于Java8导入DBCP连接池所需JAR包并编写DBCPUtils工具类...

上述五个jar包缺一不可下载解压后,进入解压出的文件夹将这五个jar包复制出来放入自己创建的myJar文件夹中(myJar文件夹创建与说明)接下来就可以在工程中进行使用package utils;import com.mysql.cj.jdbc.Driver;import org.apache.commons.dbcp2.BasicDataSource;import java.s…

C# 在自定义的控制台输出重定向类中整合调用方信息

C# 在自定义的控制台输出重定向类中整合调用方信息目录C# 在自定义的控制台输出重定向类中整合调用方信息一、前言二、输出重定向基础版三、输出重定向进阶版&#xff08;传递调用方信息&#xff09;四、后记及资源独立观察员 2021 年 1 月 6 日一、前言众所周知&#xff0c;在…

mac 上mysql怎么卸载不了_mac的mysql怎么卸载?

Mac下卸载mysql的方法&#xff1a;1、关闭mysql查看mysql是否启动&#xff1a;ps -ef |grep mysql输入&#xff1a;kill -9 (./mysqld前面第二个数字&#xff0c;这里是627) 然后回车&#xff0c;关闭mysql。2、卸载&#xff1a;在Mac终端使用下面的命令删除所有mysql文件即可s…

C#实现网页加载后将页面截取成长图片

背景最近再做一个需求&#xff0c;需要对网页生成预览图&#xff0c;如下图但是网页千千万&#xff0c;总不能一个个打开&#xff0c;截图吧&#xff1b;于是想着能不能使用代码来实现网页的截图。其实要实现这个功能&#xff0c;无非就是要么实现一个仿真浏览器&#xff0c;要…

“既然计划没有变化快,那制订计划还有个卵用啊!”

这是头哥侃码的第229篇原创每年年初&#xff0c;我的朋友圈里都会炸出不少在打完鸡血之后&#xff0c;迫不及待向全世界宣告自己 “新年Flag” 的人。有的人&#xff0c;把健身、养生设为目标&#xff0c;什么不暴瘦20斤不换头像呀&#xff0c;什么再也不吃炸鸡啤酒啦&#xff…