【B】替换 Quartz.net 默认使用的 MySql.Data 为 Mysqlconnector 的学习过程

文章转载授权级别:B

无论是 Quartz.net 还是 MySql.Data 都是我们比较熟悉的库了,Quartz.net 如果配置为使用 MySql 数据库做持久化时,默认是硬编码了使用 MySql.Data 来操作 MySql 数据库的。下面是我的一些个人诉求和实践,和大家共同探讨一下。

0x01 为什么要替换 MySql.Data

  • MySql.Data 虽然是官方驱动,但实际上实现质量并不是很高。我和一些朋友都经历过在使用它做长时间的连接查询时异常崩溃,在 老农 菊巨 的推荐以后,我一直使用 MySqlConnector 这个 MySql 驱动。

  • 因为我的项目都是使用了 MySqlConnector 这个驱动,此驱动为了兼容官方的写法,类名和命名空间基本保持一致,这就导致如果你同时引用这两个库后,再想创建 MySqlConnection 对象时,编译器无法识别到底是哪一个程序集的类型。

因此如果你项目中已经使用了 MySqlConnector,那么最好还是替换掉 Quarzt.net 默认使用的驱动。

0x02 测试 Quartz.net 使用 MySql.Data

  1. 在本地 MySql 数据库中创建测试数据库 quartz, 并通过执行脚本 https://raw.githubusercontent.com/dotnetcore/DotnetSpider/master/src/DotnetSpider.Portal/DDL/MySql.sql 来创建所需要的表

  2. 首先创建一个空的 Console 项目并添加 Quartz 这个包

  3. 修改 Program.cs 代码如下

class Program 	
{ 	public class HelloJob : IJob 	{ 	public Task Execute(IJobExecutionContext context) 	{ 	Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} Greetings from HelloJob!"); 	return Task.CompletedTask; 	} 	}	static async Task Main(string[] args)	{	IScheduler scheduler = null;	try	{	var properties = new NameValueCollection	{	{"quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},	{"quartz.jobStore.dataSource", "myDs"},	{"quartz.serializer.type", "binary"},	{"quartz.dataSource.myDs.provider", "MySql"},	{	"quartz.dataSource.myDs.connectionString",	"Database='quartz';Data Source=localhost;password=1qazZAQ!;User ID=root;Port=3306;"	}	};	scheduler = await new StdSchedulerFactory(properties).GetScheduler();	await scheduler.Start();	if (!await scheduler.CheckExists(new JobKey("job1", "group1")))	{	var trigger = TriggerBuilder.Create().WithCronSchedule("*/5 * * * * ?").WithIdentity("trigger1")	.Build();	var job = JobBuilder.Create<HelloJob>().WithIdentity("job1", "group1")	.RequestRecovery(true).Build();	await scheduler.ScheduleJob(job, trigger);	}	Console.Read();	}	finally	{	if (scheduler != null)	{	await scheduler.Shutdown();	}	}	}	}

直接运行可以发现程序异常退出了,异常信息如下:

Unhandled Exception: Quartz.SchedulerException: Could not Initialize DataSource: myDs ---> System.ArgumentException: Error while reading metadata information for provider 'MySql'
Parameter name: providerName ---> Quartz.SchedulerConfigException: Could not parse property 'connectionType' into correct data type: Could not load file or assembly 'MySql.Data, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.---> System.IO.FileNotFoundException: Could not load file or assembly 'MySql.Data, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.

可以得到明确的错误提示:未能找到 MySql.Data 程序集

  1. 引用 MySql.Data 包并重新运行程序,可以发现程序正常运行了,结果如下:

2019-09-26 22:20:46 Greetings from HelloJob!
2019-09-26 22:20:50 Greetings from HelloJob!
2019-09-26 22:20:55 Greetings from HelloJob!


0x03 测试添加 MySqlconnector

在上一小节中,我们成功的使用 MySql 数据库做为 Quartz 的持久化。在上面代码的基础之上,我们直接引用 MysqlConnector 包,看一下会有什么影响。运行后发现程序运行正常,那是不是意味着 MySql.Data 和 MysqlConnector 可以和平共处呢?如果我们想自己使用 MySqlConnection 时会发生什么呢?编译器报了如下错误:

Program.cs(23, 28): [CS0433] The type 'MySqlConnection' exists in both 'MySql.Data, Version=8.0.17.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d' and 'MySqlConnector, Version=0.59.0.0, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92'

这个结果很容易想到,因为两个程序集里有相同的命名空间,相同的类型,所以无法判断到底应该使用哪个类型。那为什么并不影响 Quartz.net 的正常运行呢?答案也是比较容易想到的:它用了反射,指定了程序集和类型。我们测试如下代码:

var ass = AppDomain.CurrentDomain.Load("MySqlConnector");
var type = ass.GetType("MySql.Data.MySqlClient.MySqlConnection");var conn = (IDbConnection) Activator.CreateInstance(	type,	"Database='dotnetspider';Data Source=my.com;password=1qazZAQ!;User ID=root;Port=3306;");

有经验的同学肯定知道以上代码是能够正常运行的。

0x04 翻看 quartz.net 的源码并尝试替换

在查看了 Quartz.net 的源码之后发现,会发现针对 MySql 的配置有如下:

# MySQL
quartz.dbprovider.MySql.productName=MySQL, MySQL provider
quartz.dbprovider.MySql.assemblyName=MySql.Data
quartz.dbprovider.MySql.connectionType=MySql.Data.MySqlClient.MySqlConnection, MySql.Data
quartz.dbprovider.MySql.commandType=MySql.Data.MySqlClient.MySqlCommand, MySql.Data
quartz.dbprovider.MySql.parameterType=MySql.Data.MySqlClient.MySqlParameter, MySql.Data
quartz.dbprovider.MySql.parameterDbType=MySql.Data.MySqlClient.MySqlDbType, MySql.Data
quartz.dbprovider.MySql.parameterDbTypePropertyName=MySqlDbType
quartz.dbprovider.MySql.parameterNamePrefix=?
quartz.dbprovider.MySql.exceptionType=MySql.Data.MySqlClient.MySqlException, MySql.Data
quartz.dbprovider.MySql.useParameterNamePrefixInParameterCollection=true
quartz.dbprovider.MySql.bindByName=true
quartz.dbprovider.MySql.dbBinaryTypeName=Blob

果然,这里 hardcode 的程序为 MySql.Data,那么我们是否可以通过修改配置文件来替换数据库驱动呢?在第一章节的测试代码中,我们添加配置后的代码如下:

var properties = new NameValueCollection
{{"quartz.dbprovider.MySql.assemblyName", "MySqlConnector"},{"quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},{"quartz.jobStore.dataSource", "myDs"},{"quartz.serializer.type", "binary"},{"quartz.dataSource.myDs.provider", "MySql"},{"quartz.dataSource.myDs.connectionString","Database='quartz';Data Source=localhost;password=1qazZAQ!;User ID=root;Port=3306;"}
};

删除干扰因素:MySql.Data 包后运行程序,发现程序异常如下:

Unhandled Exception: Quartz.SchedulerException: Could not Initialize DataSource: myDs ---> System.ArgumentException: Error while reading metadata information for provider 'MySql'
Parameter name: providerName ---> Quartz.SchedulerConfigException: Could not parse property 'connectionType' into correct data type: Could not load file or assembly 'MySql.Data, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.---> System.IO.FileNotFoundException: Could not load file or assembly 'MySql.Data, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName, ObjectHandleOnStack type, ObjectHandleOnStack keepalive)at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName)at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, Boolean loadTypeFromPartialName)at System.RuntimeType.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark)at System.Type.GetType(String typeName, Boolean throwOnError)at Quartz.Util.ObjectUtils.ConvertValueIfNecessary(Type requiredType, Object newValue) in C:\projects\quartznet\src\Quartz\Util\ObjectUtils.cs:line 69at Quartz.Util.ObjectUtils.SetPropertyValue(Object target, String propertyName, Object value) in C:\projects\quartznet\src\Quartz\Util\ObjectUtils.cs:line 200at Quartz.Util.ObjectUtils.SetObjectProperties(Object obj, NameValueCollection props) in C:\projects\quartznet\src\Quartz\Util\ObjectUtils.cs:line 151--- End of inner exception stack trace ---at Quartz.Util.ObjectUtils.SetObjectProperties(Object obj, NameValueCollection props) in C:\projects\quartznet\src\Quartz\Util\ObjectUtils.cs:line 154at Quartz.Impl.AdoJobStore.Common.EmbeddedAssemblyResourceDbMetadataFactory.GetDbMetadata(String providerName) in C:\projects\quartznet\src\Quartz\Impl\AdoJobStore\Common\EmbeddedAssemblyResourceDbMetadataFactory.cs:line 64--- End of inner exception stack trace ---at Quartz.Impl.AdoJobStore.Common.EmbeddedAssemblyResourceDbMetadataFactory.GetDbMetadata(String providerName) in C:\projects\quartznet\src\Quartz\Impl\AdoJobStore\Common\EmbeddedAssemblyResourceDbMetadataFactory.cs:line 70at Quartz.Impl.AdoJobStore.Common.DbProvider.GetDbMetadata(String providerName) in C:\projects\quartznet\src\Quartz\Impl\AdoJobStore\Common\DbProvider.cs:line 112at Quartz.Impl.AdoJobStore.Common.DbProvider..ctor(String dbProviderName, String connectionString) in C:\projects\quartznet\src\Quartz\Impl\AdoJobStore\Common\DbProvider.cs:line 74at Quartz.Impl.StdSchedulerFactory.Instantiate() in C:\projects\quartznet\src\Quartz\Impl\StdSchedulerFactory.cs:line 607--- End of inner exception stack trace ---at Quartz.Impl.StdSchedulerFactory.Instantiate() in C:\projects\quartznet\src\Quartz\Impl\StdSchedulerFactory.cs:line 616at Quartz.Impl.StdSchedulerFactory.GetScheduler(CancellationToken cancellationToken) in C:\projects\quartznet\src\Quartz\Impl\StdSchedulerFactory.cs:line 1114at quartz.demo.Program.Main(String[] args) in /Users/lewis/Documents/codes/quartz.demo/quartz.demo/Program.cs:line 44at quartz.demo.Program.Main(String[] args) in /Users/lewis/Documents/codes/quartz.demo/quartz.demo/Program.cs:line 67at quartz.demo.Program.<Main>(String[] args)

可以猜测这个配置项目并没有起作用。通过堆栈信息,我们发现错误是在 DbProvider.cs:line 74,以此为突破口逐行代码扫过去,原来 DbProvider 静态加载了2个 DbMetadataFactory,一个是通过配置文件、另一个是通过内嵌的资源文件,而配置文件它却是通过 ConfigurationManager 这个类来获取的,也就是说我们代码里的配置并没有真正的生效。于是我们可以在项目中添加 App.config 文件并修改内容为如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration><configSections><section name="quartz" type="System.Configuration.NameValueSectionHandler, System.Configuration.ConfigurationManager, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" /></configSections><quartz><add key="quartz.dbprovider.MySql.assemblyName" value="MySqlConnector" /><add key="quartz.dbprovider.MySql.commandType" value="MySql.Data.MySqlClient.MySqlCommand, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterType" value="MySql.Data.MySqlClient.MySqlParameter, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterDbType" value="MySql.Data.MySqlClient.MySqlDbType, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterDbTypePropertyName" value="MySqlDbType"/><add key="quartz.dbprovider.MySql.connectionType" value="MySql.Data.MySqlClient.MySqlConnection, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterNamePrefix" value="?"/><add key="quartz.dbprovider.MySql.exceptionType" value="MySql.Data.MySqlClient.MySqlException, MySqlConnector"/></quartz>
</configuration>

重启程序可以发现正常运行了,回过头来看代码,我们能够发现,驱动相关的配置是从内嵌配置和 App.config 中读取的,而数据库实例的配置则可以通过参数传递到 Factory 中。那么是否可以完全通过配置文件来设置呢?修改配置文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration><configSections><section name="quartz" type="System.Configuration.NameValueSectionHandler, System.Configuration.ConfigurationManager, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" /></configSections><quartz><add key="quartz.dbprovider.MySql.assemblyName" value="MySqlConnector" /><add key="quartz.dbprovider.MySql.commandType" value="MySql.Data.MySqlClient.MySqlCommand, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterType" value="MySql.Data.MySqlClient.MySqlParameter, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterDbType" value="MySql.Data.MySqlClient.MySqlDbType, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterDbTypePropertyName" value="MySqlDbType"/><add key="quartz.dbprovider.MySql.connectionType" value="MySql.Data.MySqlClient.MySqlConnection, MySqlConnector"/><add key="quartz.dbprovider.MySql.parameterNamePrefix" value="?"/><add key="quartz.dbprovider.MySql.exceptionType" value="MySql.Data.MySqlClient.MySqlException, MySqlConnector"/><add key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"/><add key="quartz.jobStore.dataSource" value="myDs"/><add key="quartz.serializer.type" value="binary"/><add key="quartz.dataSource.myDs.provider" value="MySql"/><add key="quartz.dataSource.myDs.connectionString" value="Database='quartz';Data Source=localhost;password=1qazZAQ!;User ID=root;Port=3306;"/></quartz>
</configuration>

同时代码中修改一行:

scheduler = await new StdSchedulerFactory().GetScheduler();

重启程序后得到结论:程序正常运行。

0x05 初步小结  

至此,基本上已经清楚了,因为我基本上已经不再使用 App.config,当我尝试使用 quarzt.net 从网上找到的资料大多是配置 *.properties 文件,于是为了找替代,我通过构造传入了必要参数进 factory 来构造 scheduler。但这个参数迷惑了我以为所有的配置都会从此生效,实际上是对数据库驱动相关的(DbMetadata) 是只能通过配置文件来配置。

0x06 其它方式

因为现在我们基本上都是使用 appsettings.json 来做配置了, App.config 实在是使用得少,可能有人就会对此有癖好不想添加一个新的配置文件来解决这个问题。那么接着往下分析代码,我们会发现,实际上作者提供了一个配置:

quartz.dataSource.myDs.connectionProvider.type

即我们是可以自己实际数据库相关的 Provider 的,于是我们回到第一小节的 NameCollection 中修改为只有一个配置:

{ "quartz.dataSource.myDs.connectionProvider.type", "quartz.demo.MySqlDbProvider, quartz.demo"}

同时添加如下 Provider 的实现:

public class MySqlDbProvider : IDbProvider
{public string Provider { get; set; }public MySqlDbProvider(){ConnectionString = "Database='quartz';Data Source=localhost;password=1qazZAQ!;User ID=root;Port=3306;";var metadata = new MySqlMetadata();Metadata = metadata;}public void Initialize(){}public DbCommand CreateCommand(){return new MySqlCommand();}public DbConnection CreateConnection(){return new MySqlConnection(ConnectionString);}public void Shutdown(){}public string ConnectionString { get; set; }public DbMetadata Metadata { get; }
}public class MySqlMetadata : DbMetadata
{private readonly Enum _dbBinaryType;public MySqlMetadata(){var dbBinaryTypeName = "Blob";_dbBinaryType = (Enum) Enum.Parse(Type.GetType("MySql.Data.MySqlClient.MySqlDbType, MySqlConnector"),dbBinaryTypeName);DbBinaryTypeName = dbBinaryTypeName;var parameterDbTypePropertyName = "MySqlDbType";ParameterDbTypePropertyName = parameterDbTypePropertyName;ConnectionType = Type.GetType("MySql.Data.MySqlClient.MySqlConnection, MySqlConnector");CommandType = Type.GetType("MySql.Data.MySqlClient.MySqlCommand, MySqlConnector");ParameterDbType = Type.GetType("MySql.Data.MySqlClient.MySqlDbType, MySqlConnector");ParameterType = Type.GetType("MySql.Data.MySqlClient.MySqlParameter, MySqlConnector");ParameterDbTypeProperty = ParameterType.GetProperty("MySqlDbType");if (ParameterDbTypeProperty == null){throw new ArgumentException($"Couldn't parse parameter db type for database type '{ProductName}'");}ExceptionType = Type.GetType("MySql.Data.MySqlClient.MySqlException, MySqlConnector");}public override string AssemblyName => "MySqlConnector";public override string ProductName => "MySQL, MySQL provider";public override string ParameterNamePrefix => "?";public override bool BindByName => true;public override Enum DbBinaryType => _dbBinaryType;
}

直接运行验证是不是正常。以上 Provider 实现是 hardcode 各项值,实际可以通过从 appsettings.json 中读取来设置。这样就达到:

  • 统一使用一个驱动库

  • 不添加额外配置文件

0x07 总结

开源的好处就是我们可以直接查阅代码去学习,并找出其中的关键点然后自己可以做各种各样的 workaround。.NET 在国内的情况是真的岌岌可危了,希望 .neter 能够多花时间在开源生态的建设上,少打嘴炮,多研究代码,多写代码。


640?wx_fmt=jpeg

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

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

相关文章

APM(应用性能管理)与Dapper原理介绍

一、APM&#xff08;应用性能管理&#xff09; 1.1 什么是APM&#xff1f; APM (Application Performance Management) 即应用性能管理&#xff08;应用性能监控&#xff09; APM主要是针对企业 关键业务的IT应用性能和用户体验的监测、优化&#xff0c;提高企业IT应用的可靠…

asp.netcore3.0 使用 DbProviderFactories 连接数据库

在.netstandard2.0时 System.Data.Common 这个包里并没有加入DbProviderFactoriesDbProviderFactories类在.netframework中是非常重要的存在,依靠他可以适配各种数据库客户端&#xff08;sqlserver、mysql、sqllite等&#xff09;创建数据库连接。现在可以像.netframework中一样…

MIT 6.824 Lab 1 MapReduce

MapReduce 目标 根据论文所说明的&#xff0c;有MASTER和WORKER两类工作节点&#xff0c;以下实现大都按照论文所说的实现&#xff0c;但是在对MASTER的实现上有所改动&#xff1a; MASTER向WORKER发送心跳检测&#xff0c;这里改为了对分配出去的任务进行超时监控。 MASTER…

大家在寻找的高级程序员到底是什么样子的?

你好&#xff0c;我是Z哥。这篇文章主题很简单&#xff0c;就是一个很常见的话题“什么是高级程序员&#xff1f;”。文章稍微长了些&#xff0c;但是很容易阅读。我们的中国文化&#xff0c;对“面子”看的特别重&#xff0c;所以你会发现身边到处都是高级XXX&#xff0c;听着…

优秀的程序员是那种过单行线马路都要往两边看的人

最近一周帮我以前一个同事推荐工作&#xff0c;顺便了解下行情&#xff0c;我这个同事我感觉还行&#xff0c;技术不说有多好&#xff0c;但是往年绝对不至于简历筛选时被刷掉那种&#xff0c;最先开始推给了一个我比较信任的HR手里&#xff0c;她兼职猎头&#xff0c;推给这个…

【为自己相亲】单身小姐姐你在哪里,我是书豪,我在等你

笔者简介Introduction书豪&#xff1a;【人工智能爱好者社区】公众号负责人《R数据科学实战&#xff1a;工具详解与案例分析》书籍作者。 你没看错这是书豪在给自己寻觅良缘如果你有&#xff0c;或者身边的朋友有兴趣请与我联系基本信息 出生日期&#xff1a;1995年5月身高&am…

知道的越多,越感觉自己渺小

作者&#xff1a;猛哥&#xff0c;关注技术和人文发展的程序员&#xff0c;架构师社区合伙人芝诺说&#xff1a;“人的知识就像一个圆&#xff0c;圆圈外是未知的&#xff0c;圆圈内是已知的&#xff0c;你知道的越多&#xff0c;你的圆圈就会越大。圆的周长也就越大&#xff0…

.NET Core 3.0 System.Text.Json 和 Newtonsoft.Json 行为不一致问题及解决办法

行为不一致.NET Core 3.0 新出了个内置的 JSON 库, 全名叫做尼古拉斯 System.Text.Json - 性能更高占用内存更少这都不是事...对我来说, 很多或大或小的项目能少个第三方依赖项, 还能规避多个依赖项的依赖 Newtonsoft.Json 版本不一致的问题, 是件极美的事情.但是, 结果总不是不…

Java9 新特性

在介绍 java9 之前&#xff0c;我们先来看看java成立到现在的所有版本。 1990年初&#xff0c;最初被命名为Oak&#xff1b;1995年5月23日&#xff0c;Java语言诞生&#xff1b;1996年1月&#xff0c;第一个JDK-JDK1.0诞生&#xff1b;1996年4月&#xff0c;10个最主要的操作系…

深入探究Kubernetes - 初识容器

♥2019年8月28星期三第47篇原创引言最近Kubernetes比较火&#xff0c;新技术快速火起来&#xff0c;一定有它强大的优势&#xff0c;Hr反馈&#xff0c;招聘时会Kubernetes的很少&#xff0c;风口上的Kubernetes一起学学&#xff1f;扫盲贴&#xff0c;参考《Kubernetes进阶实践…

Java11 新特性

Java 11新特性的详细解释。JDK 11已经于 2018年9月25日正式发布&#xff0c;那么Java 11主要包含哪些新特性呢&#xff1f; JDK 11是Java SE 11平台版本11的开源参考实现&#xff0c;由JSR 384在Java Community Process中指定。 阿里巴巴是中国唯一的JCP委员会成员公司&#x…

福爆 | 博客升级 .NET Core 3.0 又踩一坑

点击上方蓝字关注“汪宇杰博客”导语昨天刚发了一篇《生产大爆炸发生问题的是已经被删除的博客文章&#xff0c;正常情况下&#xff0c;这些不存在的文章会直接显示自定义的404页面&#xff0c;但实际上产生了500异常。日志如下&#xff1a;2019-09-26 00:11:50.8405|RD00155DB…

Sping5——响应式编程

1、响应式编程基础 1.1、什么是响应式编程&#xff1f; 响应式编程是一种面向数据流和变化传播的编程范式。 使用它可以在编程语言中很方便地表达静态或动态的数据流&#xff0c;而相关的计算模型会自动将变化的值通过数据流进行传播。我们可以使用声明的方式构建应用程序的能…

.NET Core使用NPOI导出复杂Word详解

最近使用NPOI做了个导出Word文档的功能&#xff0c;关于使用.NET Core 导出Word文档的方式有很多。最终我为什么选择了NPOI来实现了这个功能&#xff0c;首先是NPOI是一个开源&#xff0c;免费且容易上手的第三方框架&#xff08;并且现在已支持.NET Core&#xff0c;GitHub源码…

Spring Boot 2.0新特性

Spring Boot依赖于Spring&#xff0c;而Spring Cloud又依赖于Spring Boot&#xff0c;因此Spring Boot2.0的发布正式整合了Spring5.0的很多特性&#xff0c;同样后面Spring Cloud最新版本的发布也需要整合最新的Spring Boot2.0内容。 一、新版本特性 1.1&#xff0c;基于 Jav…

为了不让代码“作恶”,能否将道德条款纳入开源许可证?

随着特朗普政府反移民政策的执行&#xff0c;成千上万的移民儿童与父母分离&#xff0c;美国移民和海关执法局&#xff08;ICE&#xff09;也因此成为众矢之的。所以&#xff0c;当开源开发者 Seth Vargo 发现前东家 —— Chef 公司最近与 ICE 签订了合同后&#xff0c;进行删库…

dump获取与分析

一、dump基本概念 在故障定位(尤其是out of memory)和性能分析的时候&#xff0c;经常会用到一些文件来帮助我们排除代码问题。这些文件记录了JVM运行期间的内存占用、线程执行等情况&#xff0c;这就是我们常说的dump文件。常用的有heap dump和thread dump&#xff08;也叫ja…

dump分析死锁

1、制造死锁场景 一段死锁程序 public class DeadLock {public static Object lockA new Object();public static Object lockB new Object();public static void main(String[] args) {new ThreadA().start();new ThreadB().start();} }class ThreadA extends Thread {Ove…

用 C# 来守护 Python 进程

背景目前我主要负责的一个项目是一个 C/S 架构的客户端开发&#xff0c;前端主要是通过 WPF 相关技术来实现&#xff0c;后端是通过 Python 来实现&#xff0c;前后端的数据通信则是通过 MQ 的方式来进行处理。由于 Python 进程是需要依赖客户端进程来运行&#xff0c;为了保证…

针对媒体不实报道误导大众--抹黑C#工资垫底

近注意到一些媒体故意抹黑C# 工资垫底,参见 https://www.toutiao.com/i6741889572931633668/:通过搜索引擎搜索《编程语言薪酬排行&#xff1a;Python薪资最高&#xff0c;Java第二&#xff0c;C# 垫底》&#xff1a;早在2018年就出现这样的标题内容&#xff0c;还是CSDN公众号…