// 取消作业下所有触发器正在执行的程序
var scheduleResult = _schedulerFactory.TryCancelJob("job1", out var scheduler);
var scheduleResult = _schedulerFactory.TryCancelJob(scheduler);
// 新增功能:取消作业下特定触发器正在执行的程序
+ var scheduleResult = _schedulerFactory.TryCancelJob("job1", out var scheduler, "triggerId"); // Furion 4.9.2.38+ 支持+ var scheduleResult = _schedulerFactory.TryCancelJob(scheduler, "triggerId"); // Furion 4.9.2.38+ 支持
// 之前的版本只能立即执行整个作业
var scheduleResult = _schedulerFactory.TryRunJob("job1", out var scheduler);
var scheduleResult = _schedulerFactory.TryRunJob(scheduler);
// Furion 4.9.2.44+ 新增支持,可以指定触发器 Id 来立即执行特定触发器
+ var scheduleResult = _schedulerFactory.TryRunJob("job1", out var scheduler, "triggerId");+ var scheduleResult = _schedulerFactory.TryRunJob(scheduler, "triggerId");
var httpResponseModel1 = await "https://furion.net/getuser/1".PostAsync<HttpResponseModel<User>>();
- // 下列的代码会出错 ❌❌❌- var stream = await httpResponseModel1.Response.Content.ReadAsStreamAsync();- using var streamReader = new StreamReader(stream, httpResponseModel1.Encoding);
+ // 使用 using 语句确保 HttpResponseModel<T> 资源得到释放+ using var httpResponseModel1 = await "https://furion.net/getuser/1".PostAsync<HttpResponseModel<User>>();+ // 下列的代码正常啦 ✅✅✅+ var stream = await httpResponseModel1.Response.Content.ReadAsStreamAsync();+ using var streamReader = new StreamReader(stream, httpResponseModel1.Encoding);
15. 修复文件日志在一些特定情况下出现 `The stream writer is currently in use by a previous write operation.` 异常
在之前的版本中,文件日志的写入操作采用同步与异步方法混合的方式,这在某些情况下引发了The stream writer is currently in use by a previous write operation.的异常问题。为了解决这个问题,本版本进行了优化,现在完全采用异步方法进行文件日志的写入操作,以确保更高效、稳定的数据处理。
[修复] 在.NET8之后修改System.Text.Json默认序列化选项引发This JsonSerializerOptions instance is read-only or has already been used in serialization or deserialization.异常问题4.9.2.2⏱️2024.03.299f44653
蓦然回首,木已成林,.NET 框架 Furion 斩获近 1.2万⭐,v4.9.3
自上次发布以来的短短 20 天里,Furion 框架再次爆发了增长势头。在此期间,Furion 文档的注册用户激增,已突破 6 万人大关,其中近 1.9 万用户选择升级为文档会员,更有近 620 位用户开通了 VIP 会员。
同时,在 Gitee 平台上,我们收获了近 12K Stars 评价和超过 6.2K Watching,这一成绩充分证明了 Furion 框架的受欢迎程度和专业实力。
此外,我们的 NuGet 总下载量也迈过了 1600 万的里程碑,这一数字不仅是对我们努力的肯定,更是对未来发展的坚实支撑。这一系列的成绩值得我们骄傲和记录,同时也激励着我们不断前行,为 Furion 框架的未来发展继续贡献力量。
商业化半年回顾
六个月的时光匆匆而过,Furion 框架文档收费的实施也走过了其初始阶段。站在这个节点上,我想与大家分享一些感悟和体会。
首先,开源确实很棒,它打破了技术的壁垒,让知识得以自由传播。然而,开源并不意味着免费。开源项目的持续运营和维护需要巨大的投入成本,这些成本可能包括服务器费用、人力成本、时间投入等。Furion 框架亦是如此,我们一直致力于为开发者提供高质量的工具和文档,但这背后是我们团队无数日夜的辛勤付出。
文档收费的决策,我们在充分考量现有情况后,为确保 Furion 框架能够持续、健康地发展而做出的选择。我们希望通过这一方式,为团队提供稳定的资金来源,以便我们能够更加专注于框架的研发、优化以及文档的更新和维护。
这六个月里,我们听到了许多声音,有支持、有理解,也有质疑和担忧。但无论如何,我们始终坚信,只有确保了项目的持续运营和健康发展,才能更好地为开发者服务。
通过文档收费,我们得以更加专注于提升文档质量、优化用户体验。我们倾听每一位开发者的反馈和建议,努力让 Furion 框架的文档更加完善、易用。同时,我们也积极投入资源,加强技术支持和研发力度,为开发者提供更加高效、稳定的开发工具。
回顾这六个月,我们深刻体会到开源项目的不易。但我们也看到了社区的力量和潜力。感谢每一位支持 Furion 的开发者,是你们的信任让我们有动力前行。我们将继续努力,为开源社区贡献更多的力量。
蓦然回首,木已成林
基于 Furion 开源项目已在各大平台崭露头角,成为行业的翘楚。据统计,已有超过 4200 家企业选择成为 Furion 会员,并在招聘平台上发布相关职位,这些企业均采用了 Furion 进行项目开发。
借助大数据工具的分析,我们进一步发现,利用 Furion 构建的网站系统数量已超过 20 万,充分证明了其在业界的广泛认可与实际应用价值。
招聘信息:https://www.oschina.net/news/288793/furion-4-9-2-25-released
项目信息
本期亮点
随着 Furion 近期用户数量的显著增长,其需求也呈现出井喷态势,因此本次更新汇聚了众多引人注目的亮点。
1. 新增 远程请求
HttpResponseModel<T>
对象,支持携带更多返回数据问题分析
早期的
Furion
版本在处理远程请求时,提供了多样化的返回类型支持,涵盖了从原始的HttpResponseMessage
、Stream
到具体类型T
、字符串和字节数组等。如:然而,这些方法的局限性在于每次调用仅能获取单一结果,导致若需同时访问响应信息及内容时,不得不先获取
HttpResponseMessage
后再做额外转换,影响了开发效率和代码简洁度。解决方案
本次
Furion
框架的更新,引入了HttpResponseModel<T>
新类型,旨在一举解决上述难题。这一新型响应模型不仅包含了目标数据类型
T
的结果,还封装了原始的HttpResponseMessage
对象以及内容编码信息,为开发者提供了全方位的响应处理能力。无论是通过字符串拓展方法还是代理接口调用,用户现在只需一个调用即可同时获得响应体数据、响应消息对象及编码信息。通过
httpResponseModel.Response
、.Result
及.Encoding
,分别快速访问HttpResponseMessage
、数据结果及内容编码。支持
Stream、String、Byte[]
和自定义类型作为T
,确保了广泛适用性,同时明确了不支持HttpResponseMessage
自身作为T
的类型限制,防止潜在的类型循环引用问题。2. 新增
EntityFramework Core
支持批量更新设置包含或排除的属性列表问题分析
在过去版本的程序中,当进行批量更新时,系统会自动更新对象的所有字段。若实际需求仅需要更新对象的部分字段或者需要排除某些字段不进行更新,开发者则需要使用
for/foreach
循环遍历每个实体,并分别调用UpdateInclude
或UpdateExclude
方法来指定或排除需要更新的字段。这种做法不仅代码繁琐,而且效率不高。例如:
解决方案
在新版本中,我们针对这一痛点进行了优化。现在,开发者无需再为每个实体单独调用更新方法,而是可以直接在批量更新操作中通过传递参数来指定需要更新或排除的字段列表。这种方式既简洁又高效。代码示例如下:
在上面的代码中,我们使用了
_repository.Update
方法,并通过includePropertyNames
或excludePropertyNames
参数来指定需要更新或排除的字段列表。这种方式不仅简化了代码,提高了可读性,还提高了批量更新的效率。开发者只需要一次性设置参数,即可对整个实体列表进行批量更新操作。
其他说明
所有批量更新方法都添加了以上两个参数,包含
UpdateNow/UpdateAsync/UpdateNowAsync
。3. 新增定时任务可配置
RunOnStart
的逻辑问题分析
定时任务作业触发器包含一个
RunOnStart
属性配置,允许用户设置是否在程序启动时触发执行。😳😳😳 虽然这个逻辑在表面上看似合理,但问题在于它限制了用户的自由度和灵活性。
然而,我们遇到了一位有特殊需求的
VIP
用户,他们希望更加灵活地控制任务的启动执行行为。解决方案
为了满足用户的个性化需求,我们开放了
RunOnStart
的处理逻辑,引入了一个名为RunOnStartProvider
的提供器配置。通过此配置,用户可以自定义RunOnStart
的行为逻辑。例如:此外,框架底层也提供了一个默认的
RunOnStartProvider
实现,用于处理通用的启动执行逻辑:通过这一改进,我们既保留了原有逻辑的通用性,又为用户提供了足够的灵活性和扩展性,以满足各种个性化需求。
4. 新增日志模块
MessageProcess
配置,可对日志消息进行额外处理,如敏感内容脱敏在某些场景中,我们可能无意中会将敏感信息,如用户密码或手机号等,输出到日志中。为了增强数据安全性,我们可以采取以下措施进行额外的拦截处理:
在上述代码中,
脱敏处理
函数用于执行实际的脱敏操作,即替换或移除原始消息中的敏感信息。这样,即使日志被记录或查看,敏感信息也不会被泄露。通过这种方法,我们可以有效地保护用户数据的安全,避免潜在的风险。5. 新增 动态
WebAPI
支持配置默认的BindingInfo
问题分析
在
Furion
框架中,动态WebAPI
针对基元类型和字符串类型的参数,默认情况下会自动应用[FromRoute]
绑定元数据。这导致了与原生ASP.NET Core
在路由生成上的显著差异。具体示例如下:然而,原生
ASP.NET Core
生成的路由则是:这种差异导致在将旧项目迁移到 Furion 框架动态
WebAPI
时,可能会因为路由不匹配而引发错误。解决方案
为了解决这一问题,我们为动态
WebAPI
引入了DefaultBindingInfo
配置参数。该参数默认值为route
,表示基元类型和字符串类型参数将使用[FromRoute]
绑定元数据。同时,我们也提供了一个query
选项,允许开发者选择使用[FromQuery]
绑定元数据。具体配置如下:
通过上述配置,动态
WebAPI
生成的路由将与原生ASP.NET Core
保持一致,从而确保项目迁移过程中的路由兼容性。例如:通过以上优化,我们提供了更加灵活和兼容的路由配置选项,从而满足了不同项目迁移和集成的需求。
6. 改进定时任务看板,新增更多可配置特性
定时任务看板有一年没有更新了,今天花了点时间做了不少改进。
改进一:作业
Id
列固定改进二:点击作业信息列表【行】可【展开/收缩】作业触发器列表
改进三:没有作业触发器的作业部分操作按钮显示【禁用】状态
改进四:作业触发器看板文字超出自动换行
改进五:可配置作业列表【是否显示空触发器的作业】
改进六:可配置是否隐藏【页头】,方便嵌入到各种系统中
改进七:自从配置作业触发器释放默认展开
默认情况下,定时任务看板都是【收缩】状态,点击再【展开】,现在可以支持配置默认【展开】啦。
其他改进
7. 新增定时任务支持取消指定触发器正在执行的作业程序
问题分析
Furion
框架的定时任务功能虽然支持取消正在执行的作业处理程序,但这一操作目前仅能通过作业的Id
来执行。当作业包含多个触发器且它们同时运行时,使用现有的取消机制会导致所有触发器下的作业处理程序都被强制终止。今天收到了一个来自
VIP
用户的定时任务需求:从上述需求中可以看出,用户期望能够对定时任务的取消操作进行更精细化的控制,而不仅仅是基于作业
Id
。显然,现有的Furion
框架定时任务功能无法满足这一需求。解决方案
为了满足这一需求,在最新的版本更新中(
Furion 4.9.2.38+
)引入了新的取消机制,允许用户通过指定作业触发器的Id
来取消对应的作业处理程序。具体实现如下:IScheduler
接口的方式:ISchedulerFactory
接口的方式:通过这一更新,增强了
Furion
框架定时任务的灵活性和控制能力,使得用户能够更精准地管理定时任务的执行和取消。破坏性更改
JobExecutionContext.RunId
属性类型从Guid
类型调整为String
类型。8. 新增定时任务支持立即执行特定的作业触发器
问题分析
在
Furion
框架的早期版本中,存在一个功能限制:仅支持立即执行指定的作业,而无法直接触发特定作业下的特定触发器。解决方案
为了解决这一限制,我们在本次更新中实现了对特定作业触发器的直接执行功能,并同步更新了定时任务的管理界面。
如何使用新特性:
ISchedulerFactory
接口IScheduler
接口这样,开发者就能够更灵活地管理和执行定时任务了。
9. 新增日志模块设置上下文数据支持无限嵌套
在一些特别的业务场景中,我们可能需要多重设置日志上下文,如:
ScopeContext
方式BeginScope
方式现在支持这样的方式设置了。
10. 新增定时任务支持检查作业信息额外数据的键是否定义
11. 修复/改进任务队列执行方式,将队列消费速度提升百倍以上
问题分析
昨天一位
VIP
用户反馈了一个关于任务队列在动态编译JavaScript
引擎时的性能问题。用户表示,发送了超过500
条消息,但任务队列在出队时仅处理了80
多次,且处理速度异常缓慢。初步分析后,发现用户使用了
Jint
库,然而该库不支持异步和多线程操作,这导致了任务队列服务在出队消费时遭遇同步阻塞。解决方案
实际上,
Furion
框架在处理定时任务和事件总线时已经解决了类似的问题,但这一解决方案尚未应用于任务队列😳😒。现在,我们将通过TaskFactory
和Parallel
的组合来轻松解决此问题。上述代码在定时任务和事件总线中已得到验证,并成功应用于此次任务队列问题。用户提供的测试案例已通过验证,并显示出显著的性能提升。
结语
现在,任务队列的性能已至少提升了一百倍,且不再出现阻塞现象。
12. 修复 远程请求
HttpResponseModel<T>
不支持重复读Stream
问题问题分析
在上一版本中引入的
HttpResponseModel<T>
类型,当T
类型为自定义类时,存在一个潜在问题:一旦从Response.Content
流中读取数据并将其转换为T
类型后,原始流会被释放,从而阻止后续尝试再次读取该流。这会导致在尝试重复读取流时遇到“流已关闭”或“流不可读”的错误。解决方案
为了解决此问题,框架底层在读取原始响应流时采用了以下策略:
ReadAsStreamAsync
方法从response.Content
中读取流。response.Content
,以便后续可以重复读取。此过程确保了即使在将流转换为
T
类型后,原始数据仍然可以通过response.Content
访问,而不会遇到流已关闭的错误。此外,为了更好地管理资源释放,
HttpResponseModel<T>
类型现在实现了IDisposable
接口。这意味着当不再需要HttpResponseModel<T>
实例时,可以确保与其关联的流被正确释放。使用注意事项:
由于现在
HttpResponseModel<T>
实现了IDisposable
接口,因此在创建其实例时,建议使用using
语句来确保资源得到正确管理。请注意,在大多数情况下,直接从
HttpResponseModel<T>.Data
访问T
类型的数据就足够了,无需再次读取Response.Content
。如果确实需要访问原始流,则应在using
语句块内部执行此操作,以确保在流被释放之前完成所有必要的读取操作。13. 修复 模板引擎不支持将粘土对象或
DynamicObject
派生类类型设置为模板数据问题分析
过往版本的模板引擎在处理模板数据时,存在限制,仅兼容
匿名类型
、强类型
及集合类型
。这导致了当尝试使用Furion
框架的粘土对象Clay
作为模板数据源时,系统抛出异常,无法正常执行。例如:解决方案
最新提交的更新已成功解决该问题,现全面支持将粘土对象
Clay
及任何派生于DynamicObject
的类型直接作为模板数据使用。14. 修复 定时任务毫秒级间隔触发器存在严重的误差问题
问题剖析
在之前的两次提交(734a8c3 和 1756ab4)对间隔触发器的计算逻辑进行了修正,有效地解决了之前存在的误差问题。
然而,开发过程中出现了疏漏:原有的误差处理代码未能同步移除,导致系统在新逻辑执行后仍额外进行了一次误差校正操作。所以出现了间隔触发器出现预料以外的触发时间。
用户反馈
15. 修复文件日志在一些特定情况下出现 `The stream writer is currently in use by a previous write operation.` 异常
在之前的版本中,文件日志的写入操作采用同步与异步方法混合的方式,这在某些情况下引发了
The stream writer is currently in use by a previous write operation.
的异常问题。为了解决这个问题,本版本进行了优化,现在完全采用异步方法进行文件日志的写入操作,以确保更高效、稳定的数据处理。16. 修复定时任务看板触发器文字过多出现超出布局情况
旧版本
新版本
本期更新
新特性
JWT
授权出现代码异常 4.9.3 ⏱️2024.05.10 52d3c2c edc51f4SHA1
加密和比较功能的静态类和字符串拓展支持 4.9.2.41 ⏱️2024.05.08 @superbisu !879 f592757DefaultExpandAllJobs
4.9.2.40 ⏱️2024.05.07 77c1e6fDisplayEmptyTriggerJobs
和是否显示页头DisplayHead
4.9.2.39 ⏱️2024.05.07 f64d45fClay
转换为可枚举对象AsEnumerable()
方法 4.9.2.37 ⏱️2024.05.06 b1c8fa4HttpResponseModel<T>
类型,包含HttpResponseMessage
、返回值等属性 4.9.2.34 ⏱️2024.04.30 42ccdaaId
集合做批量删除操作 4.9.2.33 ⏱️2024.04.30 d01a6e7WebAPI
支持配置基元类型和字符串类型默认绑定信息 4.9.2.32 ⏱️2024.04.28 d7e7a02ContainsProperty(key)
方法 4.9.2.32 ⏱️2024.04.28 71f97f0MessageProcess
配置,可对日志消息进行额外处理,如敏感内容脱敏 4.9.2.32 ⏱️2024.04.28 0d9ff5eRunOnStart
的处理逻辑options.RunOnStartProvider
4.9.2.29 ⏱️2024.04.23 c9e0e3eEFCore
批量更新支持设置includePropertyNames
和excludePropertyNames
参数 4.9.2.28 ⏱️2024.04.23 c9926cc[BaseAddress]
特性快速设置HttpClient
客户端BaseAddress
4.9.2.25 ⏱️2024.04.19 ea88c95JsonSerializerOptions
序列化配置 4.9.2.24 ⏱️2024.04.17 cc6dd13WebAPI
支持贴[Route]
特性动态生成控制器 4.9.2.19 ⏱️2024.04.16 #I9H1QHAES
加解密支持向量IV
、模式Mode
和填充Padding
配置 4.9.2.18 ⏱️2024.04.15 d549bbaISchedulerFactory
启停作业StartJob
和PauseJob
方法 4.9.2.16 ⏱️2024.04.11 89061efAppSettings
拓展程序集ExternalAssemblies
配置支持目录扫描 4.9.2.14 ⏱️2024.04.10 e68f0a6.GroupSet
方法 4.9.2.9 ⏱️2024.04.09 9e08278WebAPI
方法添加[DisplayName]
特性生成Swagger
文档注释 4.9.2.3 ⏱️2024.03.30 0f24c66突破性变化
IUnifyResultProvider
接口,新增OnAuthorizeException
方法 4.9.3 ⏱️2024.05.10 52d3c2c edc51f4AppAuthorizeHandler
接口的HandleAsync
方法签名,新增DefaultHttpContext
参数 4.9.3 ⏱️2024.05.10 52d3c2c edc51f4DES
加解密相关类和方法命名:DESCEncryption
->DESEncryption
,ToDESCEncrypt
->ToDESEncrypt
,ToDESCDecrypt
->ToDESDecrypt
4.9.2.41 ⏱️2024.05.08 a46f129RunId
类型,由Guid
改为string
类型 4.9.2.38 ⏱️2024.05.07 5aa20b5Swagger
文档注释逻辑,将///
注释方式优先级调整至最高,可覆盖[DisplayName]
特性方式 4.9.2.17 ⏱️2024.04.14 ba5249cTryRunJob
方法签名,追加out IScheduler scheduler
参数 4.9.2.16 ⏱️2024.04.11 89061ef问题修复
bug
4.9.2.40 ⏱️2024.05.07 77c1e6fDynamicObject
派生类类型设置为模板数据 4.9.2.36 ⏱️2024.05.05 07ee172options.MessageProcess
无效问题 4.9.2.36 ⏱️2024.05.05 b5cb0feHttpResponseModel<T>
不支持重复读Response.Content
流问题 4.9.2.35 ⏱️2024.04.30 7ca0650Monitor
捕获异常时因其StackTrace
堆栈信息可能为null
引发的空异常问题 4.9.2.31 ⏱️2024.04.25 @xjj_0906 !875 7621e75RunOnStart
和StartTime
考虑场景 4.9.2.30 ⏱️2024.04.23 2595379 7ac6a54The stream writer is currently in use by a previous write operation.
异常 4.9.2.27 ⏱️2024.04.22 3ca012bWebAPI
不支持[BindNever]
特性忽略路由和Action
参数设置 4.9.2.25 ⏱️2024.04.19 21599e6Monitor
不支持粘土对象Clay/dynamic
类型格式化输出 4.9.2.24 ⏱️2024.04.17 d578cfbXElement
属性包含type="null"
节点出现异常问题 4.9.2.21 ⏱️2024.04.16 9d5870fWebAPI
错误将CancellationToken
类型当作路由参数 4.9.2.19 ⏱️2024.04.16 #I9H14XGroupSet
功能影响到了原有的SetGroupName
逻辑 4.9.2.15 ⏱️2024.04.11 #I9FOU0 9e08278PostgreSQL
数据库SQL
语句的字段名缺少"
双引号 4.9.2.13 ⏱️2024.04.10 #I9FD9YJobBuilder
构建委托作业永远无法执行问题 4.9.2.10 ⏱️2024.04.10 Sundial#I7KU7K401/403
等状态码中间件时进行了错误拦截 4.9.2.8 ⏱️2024.04.08 b135e8cJWT Token
时如果Bearer
后面跟多个空格导致验证失败问题 4.9.2.8 ⏱️2024.04.08 @xuejf168 !874SQL
查询结果转模型不支持DateOnly
和TimeOnly
属性类型 4.9.2.7 ⏱️2024.04.04 31f9d23Object
类型设置给Array
类型出现递归死循环问题 4.9.2.5 ⏱️2024.04.03 1126c74.NET8
之后修改System.Text.Json
默认序列化选项引发This JsonSerializerOptions instance is read-only or has already been used in serialization or deserialization.
异常问题 4.9.2.2 ⏱️2024.03.29 9f44653IHttpDispatchProxy
模式配置重试策略无效 4.9.2.1 ⏱️2024.03.29 #I9CK7X其他更改
IPC
管道消息消费方式由无序改为有序 4.9.2.33 ⏱️2024.04.30 ce59c3aSystem.Text.Json
序列化提供器选项为不区分大小写匹配 4.9.2.1 ⏱️2024.03.29 b58e7be文档
FS
静态类文档、序列化文档、模块化文档、数据加解密文档、动态WebAPI
文档、IPC
通信模块文档、日志文档、安全授权文档贡献者