❤️ 关注 Furion 微信公众号有惊喜哦!
Skip to main content

1.6 更新日志

Furion 框架升级/发版规则

升级前重点关注可能造成【破坏性】的标签类型

版本号规则:主版本号.次版本号.修订版本号

  • 只要【确认】为框架 bug,则当天修复,当天发版,修订版本号 加 1
  • 只要 .NET SDK 版本更新,则当天升级,当天发版,修订版本号 加 1
  • 如果 .csproj 文件有变更,则当天发版,修订版本号 加 1
  • 如果新增 拓展包,为了版本号统一,则当天发版,修订版本号 加 1
  • 如果涉及到代码重构,则当天发版,次版本号 加 1,修订版本号 清 0
  • 如果 .NET SDK 主版本号升级,则当天发版,主版本号 加 1

如有意外不能当天发版,则会在 Issue 中说明具体发版时间,正常不会超过 3 天。

开通 VIP 服务助力 Furion v5 发布
308
1000

v4.9.1(当前版本,.NET8)

版本细节

⭐️ .NET8 升级指南:http://furion.baiqian.ltd/docs/net7-to-net8 ⭐️

  • 新特性

  • 突破性变化

    •   框架底层适配 .NET8.0 正式版 4.9.1 ⏱️2023.11.15
    •   框架脚手架适配 .NET8.0 正式版 4.9.1 ⏱️2023.11.15

v4.8.8(已发布)

版本细节
  • 新特性

    •   Db.GetNewDbContext() 多个重载方法,实现类似 new DbContext() 操作 4.8.8.55 ⏱️2023.11.09 4157629
    •   控制台日志 AddConsoleFormatter 服务支持 WriteFilter 属性过滤 4.8.8.52 ⏱️2023.11.07 516acb4
    •   监听日志 LoggingMonitor 支持打印输出 requestHeaders 请求头信息 4.8.8.50 ⏱️2023.10.27 #I8BHM3
    •   多语言支持 L.GetDefaultCulture() 获取本地配置默认语言 4.8.8.49 ⏱️2023.10.25 !858
    •   定时任务 Http 作业请求头 Headers 和作业分组 Group 和描述 Description 支持 4.8.8.46 ⏱️2023.10.09 #I85Z7S
    •   定时任务看板列表支持作业分组名排序 4.8.8.43 ⏱️2023.09.14 #I7YQ9V
    •   验证特性 [DataValidation] 支持 [Display][DisplayName] 特性设置 {0} 4.8.8.42 ⏱️2023.09.01 #I7XB3T
    •   监听日志 LoggingMonitor 支持配置日志输出级别 4.8.8.41 ⏱️2023.08.25 #I7SRTP
    •   多语言支持 L.GetString(name, culture) 获取指定区域翻译 4.8.8.41 ⏱️2023.08.04 044b0ed
    •   粘土对象 .ConvertTo 支持自定义值提供器 4.8.8.40 ⏱️2023.08.03 70d5888
    •   规范化文档枚举支持 [EnumToNumber] 特性配置生成前端枚举定义代码是字符串值还是整数值类型,默认为字符串值 4.8.8.35 ⏱️2023.07.06 #I7IZ7S
    •   定时任务作业计划 OnChanged 事件处理 4.8.8.29 ⏱️2023.06.25 e4c4cf1
    •   Swagger 分组信息可在任意配置文件中通过 [openapi:分组名] 进行配置 4.8.8.26 ⏱️2023.06.20 a70eed3
    •   TP.WrapperRectangle 绘制矩形日志模板 4.8.8.25 ⏱️2023.06.14 60ffd76
    •   IServiceScope.CreateDefaultHttpContext 拓展方法 4.8.8.24 ⏱️2023.06.07 11a55e1
    •   配置模块 IgnoreConfigurationFiles 支持完整的文件通配符 4.8.8.22 ⏱️2023.05.25 #I78ABL
    •   定时任务支持二级虚拟目录 VisualPath 配置部署 4.8.8.20 ⏱️2023.05.18 #I740IA
    •   监听日志 LoggingMonitor 支持 Razor Pages 4.8.8.16 ⏱️2023.05.15 #I7332C
    •   定时任务作业处理程序工厂 IJobFactory 支持 4.8.8.13 ⏱️2023.05.08 ad58dd3
    •   AES 支持对文件(含超大文件)进行加解密 4.8.8.11 ⏱️2023.05.05 1d2265b
    •   动态 WebAPI 支持 text/plain 格式的 Body 参数 4.8.8.9 ⏱️2023.05.04 b49fe50
    •   插件化 IDynamicApiRuntimeChangeProvider 接口,可在运行时动态添加 WebAPI/Controller 4.8.8.8 ⏱️2023.05.04 322ea59
查看变化

在一些特定的需求中,我们需要在运行时动态编译代码,如动态编写 WebAPI,之后能够在不重启主机服务的情况下即可有效。比如这里动态添加 SomeClass 动态 WebAPI,然后在 Swagger/路由系统 中立即有效:

using Furion;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace YourProject.Application;

public class PluginApiServices : IDynamicApiController
{
private readonly IDynamicApiRuntimeChangeProvider _provider;
public PluginApiServices(IDynamicApiRuntimeChangeProvider provider)
{
_provider = provider;
}

/// <summary>
/// 动态添加 WebAPI/Controller
/// </summary>
/// <param name="csharpCode"></param>
/// <param name="assemblyName">可自行指定程序集名称</param>
/// <returns></returns>
public string Compile([FromBody] string csharpCode, [FromQuery] string assemblyName = default)
{
// 编译 C# 代码并返回动态程序集
var dynamicAssembly = App.CompileCSharpClassCode(csharpCode, assemblyName);

// 将程序集添加进动态 WebAPI 应用部件
_provider.AddAssembliesWithNotifyChanges(dynamicAssembly);

// 返回动态程序集名称
return dynamicAssembly.GetName().Name;
}

/// <summary>
/// 移除动态程序集 WebAPI/Controller
/// </summary>
public void Remove(string assemblyName)
{
_provider.RemoveAssembliesWithNotifyChanges(assemblyName);
}
}

这时只需要请求 api/plugin-api/compile 接口同时设置请求 Content-Typetext/plain,接下来传入 C# 代码字符串 即可,如:

动态C#代码字符串
using Furion.DynamicApiController;

namespace YourProject.Application;

public class SomeClass : IDynamicApiController
{
public string GetName()
{
return nameof(Furion);
}
}

之后刷新浏览器即可看到最新的 API

还可以在运行时动态卸载,使用 DELETE 请求 api/plugin-api 即可。

    •   定时任务 Schedular.CompileCSharpClassCode(code) 支持动态编译作业处理程序代码 4.8.8.7 ⏱️2023.04.30 fe1e8a1
    •   App.CompileCSharpClassCode(code) 动态编译类定义代码 4.8.8.7 ⏱️2023.04.30 fe1e8a1
    •   粘土对象支持结构 struct 对象类型 4.8.8.7 ⏱️2023.04.30 a0fa3aa
    •   定时任务支持配置 IJob 执行异常 FallbackAsync 回退策略 4.8.8.6 ⏱️2023.04.25 7671489
    •   定时任务支持在非 IOC/DI 项目类型中使用 4.8.8.5 ⏱️2023.04.24 #I6YJNB
    •   RSA 支持对超长字符(超 245 位)进行分段加解密 4.8.8.2 ⏱️2023.04.19 !788 感谢 @YaChengMu
    •   System.Text.JsonNewtonsoft.Json 对粘土对象 Clay 支持 4.8.8.1 ⏱️2023.04.18 #I6WKRZ
    •   粘土对象可反射转换成特定 IEnumerable<T> 类型:clay.ConvertTo<T>() 4.8.8 ⏱️2023.04.13 5d54a65
    •   Serve.IdleHost 支持返回 httphttps 协议 Web 地址(端口) 4.8.8 ⏱️2023.04.13 fdf7885
  • 突破性变化

    •   定时任务看板 SyncRate 配置,前后端采用最新的 SSE 推送技术替代 4.8.8.29 ⏱️2023.06.25 e4c4cf1
    •   监听日志 WriteFilterConfigureLoggerActionExecutingContextActionExecutedContext 类型为 FilterContext 4.8.8.16 ⏱️2023.05.15 #I7332C
    •   IJsonSerializerProvider 序列化接口,添加 Deserialize 反序列化方法 4.8.8.15 ⏱️2023.05.15 !815 感谢 @YaChengMu
查看变化

添加 25-32行 接口方法:

namespace Furion.JsonSerialization;

/// <summary>
/// Json 序列化提供器
/// </summary>
public interface IJsonSerializerProvider
{
/// <summary>
/// 序列化对象
/// </summary>
/// <param name="value"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
string Serialize(object value, object jsonSerializerOptions = default);

/// <summary>
/// 反序列化字符串
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
T Deserialize<T>(string json, object jsonSerializerOptions = default);

/// <summary>
/// 反序列化字符串
/// </summary>
/// <param name="json"></param>
/// <param name="returnType"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
object Deserialize(string json, Type returnType, object jsonSerializerOptions = default);

/// <summary>
/// 返回读取全局配置的 JSON 选项
/// </summary>
/// <returns></returns>
object GetSerializerOptions();
}

如果使用 Newtonsoft.Json 则只需添加以下实现即可:

/// <summary>
/// 反序列化字符串
/// </summary>
/// <param name="json"></param>
/// <param name="returnType"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
public object Deserialize(string json, Type returnType, object jsonSerializerOptions = null)
{
return JsonConvert.DeserializeObject(json, returnType, (jsonSerializerOptions ?? GetSerializerOptions()) as JsonSerializerSettings);
}
  • 问题修复

    •   远程请求获取响应 Cookies 被截断问题 4.8.8.54 ⏱️2023.11.08 #I8EV1Z
    •   远程请求上传文件在其他编程语言获取文件名存在双引号问题 4.8.8.53 ⏱️2023.11.07 #I8EF1S
    •   定时任务高频作业下持久化操作出现阻塞卡问题 4.8.8.51 ⏱️2023.11.06 f1d0b4a
    •   定时任务看板中间件 SSE 请求不是长连接导致连接频繁初始化销毁 4.8.8.49 ⏱️2023.10.26 1997f1b
    •   动态 WebAPI 不能正确移除 AppService 命名的 Service 问题 4.8.8.47 ⏱️2023.10.10 #I86NL
    •   审计日志不支持 dynamic/JsonElement 序列化问题 4.8.8.45 ⏱️2023.09.29 #I84SD5
    •   Scoped.CreateUowAsync 作用域工作单元异常无法回滚问题 4.8.8.44 ⏱️2023.09.23 #I833I9
    •   模板引擎高并发读取缓存模板出现线程占用问题 4.8.8.43 ⏱️2023.09.14 #I80ZKB
    •   使用刷新 Token 也能通过鉴权检查严重安全 Bug 4.8.8.42 ⏱️2023.08.28 #I7TII4
    •   粘土对象不支持枚举类型问题 4.8.8.41 ⏱️2023.08.25 #I7VDDL
    •   定时任务因上一版本修改 4e2615b 导致自定义作业触发器异常问题 4.8.8.36 ⏱️2023.07.06 #I7J59D
    •   审计日志解析 DateTime 类型参数不是本地时间问题 4.8.8.33 ⏱️2023.06.29 #I7GW32
    •   定时任务因上一版本修改 4e2615b 导致 Cron 解析异常问题 4.8.8.32 ⏱️2023.06.28 #I7GQ5I
    •   定时任务设置额外数据不支持 long/int64 类型参数问题 4.8.8.31 ⏱️2023.06.28 4e2615b
    •   定时任务休眠毫秒数大于 int.MaxValue 时出现 ArgumentOutOfRangeException 4.8.8.27 ⏱️2023.06.21 #I7F6ZT
    •   Cron 表达式步长解析器错误 4.8.8.25 ⏱️2023.06.14 #I7D9XU
    •   修复 ExpandoObject.ToDictionary() 转换异常 4.8.8.25 ⏱️2023.06.14 #I7BY0P
    •   配置友好异常 FriendlyExceptionSettings:DefaultErrorMessage 无效问题 4.8.8.23 ⏱️2023.05.31 #I79LIG
    •   Swagger 进行分组后 Tags 不能进行分组过滤问题 4.8.8.22 ⏱️2023.05.25 #I78A55
    •  9d8cb82 代码提交导致命名服务解析异常问题 4.8.8.21 ⏱️2023.05.18 #I76JZR
    •  9d8cb82 代码提交导致服务 AOP 异常拦截问题 4.8.8.17 ⏱️2023.05.15 #I73A8E
    •   动态 WebAPI 自定义路由模板参数和自动拼接参数冲突问题 4.8.8.15 ⏱️2023.05.15 #I72ZZ2
    •   远程请求在被请求端返回非 200 状态码但实际请求已处理也抛异常问题 4.8.8.14 ⏱️2023.05.12 b14a51f
    •   App.CompileCSharpClassCode(code) 运行时添加匿名程序集编译异常问题 4.8.8.8 ⏱️2023.05.04 322ea59
    •   LoggingMonitor 打印泛型类型如果存在多个泛型参数问题 4.8.8.8 ⏱️2023.05.04 8d9cb74
    •   脱敏处理如果字典存在重复词导致异常问题 4.8.8.4 ⏱️2023.04.23 #I6Y19K
    •   远程请求 Body 参数为粘土对象 Clay 类型序列化有误 4.8.8.1 ⏱️2023.04.18 #I6WKRZ
    •   Serve.IdleHost 获取随机端口的本地地址带 $ 符号问题 4.8.8 ⏱️2023.04.13 ed6f292
  • 其他更改

    •   [UnitofWork] 支持在 Class 中指定,解决 Pages 应用警告问题 4.8.8.42 ⏱️2023.09.01 #I7X51E
    •   取消远程请求 GET/HEAD 不能传递 Body 的限制 4.8.8.39 ⏱️2023.08.02 8113460
    •   规范化文档枚举生成 json 格式,由 int32 改为 string 4.8.8.34 ⏱️2023.07.02 #I7HOPR
    •   规范化文档默认 Title 解析规则,不再自动添加空格 4.8.8.26 ⏱️2023.06.20 24b7a47
    •   组件 Component 模式支持 [DependsOn] 支持继承 4.8.8.16 ⏱️2023.05.15 #I733RF
    •   定时任务 GC 回收逻辑,避免高频添加作业导致 尾延迟 问题 4.8.8.3 ⏱️2023.04.21 #I6XIV8
    •   定时任务日志设计,减少不必要的日志输出 4.8.8.3 ⏱️2023.04.21 #I6XI2L
  • 文档

    •   Jwt 身份验证过程监听文档
    •   事件总线 Redis 集成文档
    •   粘土对象文档、虚拟文件系统文档、序列化文档、事件总线文档、远程请求文档、数据加密文档、安全授权文档、动态 WebAPI 文档、定时任务文档、JSON 序列化文档、App 静态类文档、规范化文档、配置文档、数据库上下文文档、Db 静态类文档
  • 贡献者


v4.8.7(已发布)

.NET8 Preview.1 发布

🚀🎉🔥 2023 年 02 月 22 日,微软发布了 .NET8 首个预览版。

https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-1/

Furion 第一时间完成了适配,v4 版本开始一套代码支持 .NET5-.NET8/N,支持所有 Furion 版本升级

版本细节
  • 新特性

    •   定时任务看板支持自定义刷新频率 SyncRate 功能 4.8.7.43 ⏱️2023.04.12 703b465
    •   Serve.GetIdleHost([host]) 静态方法,可获取一个指定主机的 Web 地址(端口) 4.8.7.43 ⏱️2023.04.12 fdf788
    •   粘土对象可配置访问不存在 Key 时是抛异常还是返回 null 4.8.7.40 ⏱️2023.04.10 e994d53
    •   定时任务看板支持完全自定义 RequestPath 入口地址功能 4.8.7.34 ⏱️2023.04.04 24736f6
    •   App.GetServices(type)App.GetServices<T>() 获取服务实例集合 4.8.7.33 ⏱️2023.04.03 c3e9957
    •   远程请求 [HttpMethod]ToSaveAsync 下载远程文件并保存到磁盘方法 4.8.7.32 ⏱️2023.04.02 bfd02c1
    •   定时任务一系列 .AlterTo 修改作业触发器触发时间便捷方法 4.8.7.31 ⏱️2023.03.31 0349017
    •   多语言支持 DateTime 时间格式化配置节点 DateTimeFormatCulture 4.8.7.31 ⏱️2023.03.31 #I6RUOU
    •   Serve.IdleHost 静态属性,可获取一个随机空闲 Web 主机地址(端口) 4.8.7.29 ⏱️2023.03.30 e425063
    •   WinForm/WPF 静态方法 Serve.RunNative() 可配置是否启用 Web 主机功能 4.8.7.26 ⏱️2023.03.29 #I6R97L
    •   WinForm/WPF 支持依赖注入的 Native.CreateInstance<T>() 静态方法 4.8.7.23 ⏱️2023.03.27 53d51c3
    •   WinForm/WPF 快速注册静态方法:Serve.RunNative() 4.8.7.23 ⏱️2023.03.27 53d51c3
    •   远程请求支持 Content-Typetext/htmltext/plain 处理 4.8.7.22 ⏱️2023.03.27 #I6QMLR
    •   粘土对象可转换成 IEnumerable<T> 对象并实现 Lambda/Linq 操作 4.8.7.19 ⏱️2023.03.22 2b14ed9
查看变化
dynamic clay = Clay.Parse("{\"Foo\":\"json\",\"Bar\":100,\"Nest\":{\"Foobar\":true},\"Arr\":[\"NOR\",\"XOR\"]}");

// 将 clay.Arr 转换成 IEnumerable<dynamic>
IEnumerable<dynamic> query = clay.Arr.AsEnumerator<dynamic>();

// 实现 Lambda/Linq 操作
var result = query.Where(u => u.StartsWith("N"))
.Select(u => new
{
Name = u
})
.ToList();
    •   Crontab.IsValid(...) 静态方法,判断 Cron 表达式是否有效 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   日志配置 WithStackFrame,可控制是否输出产生日志的程序集,类型和具体方法 4.8.7.16 ⏱️2023.03.19 5ad6ae2
查看变化

启用 WithStackFrame 日志配置后,可输出程序集,类型,方法签名信息。

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.WithStackFrame = true;
});

// 文件日志
services.AddFileLogging(options =>
{
options.WithStackFrame = true;
});

// 数据库日志
services.AddDatabaseLogging(options =>
{
options.WithStackFrame = true;
});

日志输出如下:

info: 2023-03-17 18:25:06.7988349 +08:00 星期五 L System.Logging.EventBusService[0] #1
[Furion.dll] async Task Furion.EventBus.EventBusHostedService.ExecuteAsync(CancellationToken stoppingToken)
EventBus hosted service is running.
info: 2023-03-17 18:25:08.1393952 +08:00 星期五 L Microsoft.Hosting.Lifetime[14] #1
[System.Private.CoreLib.dll] void System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<TStateMachine>(ref TStateMachine stateMachine)
Now listening on: https://localhost:5001
info: 2023-03-17 18:25:08.1620391 +08:00 星期五 L Microsoft.Hosting.Lifetime[14] #1
[System.Private.CoreLib.dll] void System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<TStateMachine>(ref TStateMachine stateMachine)
Now listening on: http://localhost:5000
info: 2023-03-17 18:25:08.1972456 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[Microsoft.Extensions.Hosting.dll] void Microsoft.Extensions.Hosting.Internal.ConsoleLifetime.OnApplicationStarted()
Application started. Press Ctrl+C to shut down.
info: 2023-03-17 18:25:08.2456579 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[Microsoft.Extensions.Hosting.dll] void Microsoft.Extensions.Hosting.Internal.ConsoleLifetime.OnApplicationStarted()
Hosting environment: Development
info: 2023-03-17 18:25:08.2746134 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[System.Private.CoreLib.dll] void System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(bool throwOnFirstException)
Content root path: D:\Workplaces\OpenSources\Furion\samples\Furion.Web.Entry
info: 2023-03-17 18:25:18.1917784 +08:00 星期五 L Furion.Application.TestLoggerServices[0] #16
[Furion.Application.dll] void Furion.Application.TestLoggerServices.测试日志()
我是一个日志 20

这样就清楚地知道日志是哪个程序集、哪个类型、哪个方法输出的了。

    •   定时任务看板 UI 作业列表 最近执行时间 列和优化显示效果 4.8.7.12 ⏱️2023.03.15 26462a8 cb5dd17
    •   定时任务作业计划/工厂立即执行 RunJob 方法 4.8.7.11 ⏱️2023.03.15 #I6LD9X
    •   定时任务看板 UI 提供立即执行功能 4.8.7.11 ⏱️2023.03.15 #I6LD9X
    •   远程请求 HttpRequestMessage 拓展方法 AppendHeaders 4.8.7.10 ⏱️2023.03.14 #I6MVHT
    •   定时任务作业执行上下文 JobExecutionContext 服务提供器 ServiceProvider 属性 4.8.7.10 ⏱️2023.03.14 02586f8
    •   定时任务 HTTP 作业,支持定时请求互联网 URL 地址 4.8.7.7 ⏱️2023.03.11 01d4466
查看变化
services.AddSchedule(options =>
{
options.AddHttpJob(request =>
{
request.RequestUri = "https://www.chinadot.net";
request.HttpMethod = HttpMethod.Get;
// request.Body = "{}"; // 设置请求报文体
}, Triggers.PeriodSeconds(5));
});
    •   定时任务作业触发器 Trigger 执行结果 Result 和执行耗时 ElapsedTime 属性 4.8.7.7 ⏱️2023.03.11 01d4466
    •   定时任务作业看板支持查看作业触发器执行结果 Result 和执行耗时 ElapsedTime 属性 4.8.7.7 ⏱️2023.03.11 01d4466
    •   定时任务休眠时长和唤醒时机日志输出 4.8.7.6 ⏱️2023.03.08 #I6LANE
    •   Sql 高级拦截支持返回 IEnumerable<T>T[] 类型值 4.8.7.5 ⏱️2023.03.07 f2ca2d3
查看变化

过去版本如果返回对象类型只支持 List<T>TTuple<>,现已支持 IEnumerable<T>T[]Tuple<> 混合体。

public interface ISql : ISqlDispatchProxy
{
[SqlExecute("select * from person")]
Person[] GetPersons();

[SqlExecute("select * from person")]
IEnumerable<Person> GetPersons2();

// 更复杂的组合
[SqlExecute(@"
select * from person where id = 1;
select * from person;
select * from person where id > 0;
select * from person where id > 0;
")]
(Person, List<Person>, Person[], IEnumerable<Person>) GetPersons();
}
    •   .m3u8.ts 文件类型 MIME 支持 4.8.7.5 ⏱️2023.03.07 #I6KKEM
    •   审计日志 LoggingMonitor 支持对参数贴 [SuppressMonitor] 特性跳过记录 4.8.7.3 ⏱️2023.03.01 #I6IVGW
    •   审计日志 LoggingMonitor 监听 TraceIdThreadIdAccept-Language 4.8.7.1 ⏱️2023.02.27 df35201
    •   规范化结果 UnifyContext.GetSerializerSettings(string) 静态方法 4.8.7.1 ⏱️2023.02.27 #I6HM7T
  • 突破性变化

    •   定时任务动态作业 DynamicJob 委托/方法签名 4.8.7.10 ⏱️2023.03.14 6d56b53
查看变化

减少记忆负担,统一动态作业和普通作业的 ExecuteAsync 方法签名,故做出调整。

由:

options.AddJob((serviceProvider, context, stoppingToken) =>
{
serviceProvider.GetLogger().LogInformation($"{context}");
return Task.CompletedTask;
}, Triggers.PeriodSeconds(5));

调整为:

options.AddJob((context, stoppingToken) =>
{
context.ServiceProvider.GetLogger().LogInformation($"{context}");
return Task.CompletedTask;
}, Triggers.PeriodSeconds(5));
    •   适配 .NET8 Preview.1 4.8.7 ⏱️2023.02.22
    •   脚手架支持创建 .NET8 Preview.1 项目 4.8.7 ⏱️2023.02.22
  • 问题修复

    •   远程请求获取 Cookies 时如果包含相同 Key 异常问题 4.8.7.44 ⏱️2023.04.12 #I6V3T7
    •   粘土对象转换为 Dictionary<string, object> 类型异常 4.8.7.41 ⏱️2023.04.11 f96baeb
    •   TP.Wrapper 静态类不能准确识别多行内容问题 4.8.7.40 ⏱️2023.04.10 #I6UAC8
    •   粘土对象不支持运行时动态设置携带特殊字符的 Key4.8.7.39 ⏱️2023.04.10 6572515
    •   视图引擎模型为匿名泛型集合类型时出现类型转换异常 4.8.7.38 ⏱️2023.04.07 !773
    •   定时任务通过作业 Id 删除作业不能删除作业触发器问题 4.8.7.35 ⏱️2023.04.05 312ca35
    •   动态 WebAPI 去除叠词类型命名如 ServiceService 前后缀异常问题 4.8.7.32 ⏱️2023.04.02 #I6SB3Z
    •  4.8.7.22 版本导致动态 WebAPI 类型注释丢失问题 4.8.7.27 ⏱️2023.03.29 #I6QM23
    •   粘土对象遍历对象键值对因 4.8.7.19 版本更新导致异常 4.8.7.25 ⏱️2023.03.28 #I6R4ZU
    •   Swagger UI 不显示 ControllerBase 派生类注释 4.8.7.22 ⏱️2023.03.27 #I6QM23
    •   日志输出 JSON 格式漏掉了 UseUtcTimestampTraceId 键值 4.8.7.21 ⏱️2023.03.27 5c90e65
    •   启用规范化结果后导致 WebSocket 连接断开时出现异常 4.8.7.20 ⏱️2023.03.23 #I6PI5E
    •   定时任务作业状态为 积压:0归档:6 时调用立即执行后不能恢复上一次状态 4.8.7.18 ⏱️2023.03.21 6f5aae8
    •   使用达梦数据库执行 sql 不能自动修复命令参数前缀 4.8.7.18 ⏱️2023.03.21 #I6OK4T
    •   Cron 表达式 * 符号解析器不够严谨,如:*1111aaaaa 也被解析为 * 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   定时任务更新作业 null 值默认被跳过问题 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   视图引擎不支持强制转换的 (object)model 类型 4.8.7.16 ⏱️2023.03.19 #I6O3BD
    •   启用请求 Body 重复读且在授权之前读取导致非 GET/HEAD/OPTION 请求异常 4.8.7.15 ⏱️2023.03.19 #I6NX9E
    •   定时任务生成 SQL 语句没有处理 ' 转义问题 4.8.7.15 ⏱️2023.03.19 #I6NXKA
    •   数据验证 ValiationTypes.GUID_OR_UUID 不支持大写问题 4.8.7.14 ⏱️2023.03.16 #I6NP22
    •   Blazor 脚手架出现 blazor.server.js 不能加载问题(4044.8.7.13 ⏱️2023.03.16 #I6NOBQ
    •   定时任务服务在停止进程时会卡住 30秒 问题 4.8.7.8 ⏱️2023.03.13 #I6MI9I #I6MHOU
    •   定时任务看板删除不存在的作业触发器出现空异常 4.8.7.7 ⏱️2023.03.11 01d4466
    •   日志消息没有处理 \n 换行符对齐问题 4.8.7.6 ⏱️2023.03.10 759bcc5
    •   审计日志 LoggingMonitor 对特定参数贴有 [FromServices] 特性依旧记录问题 4.8.7.3 ⏱️2023.03.01 17b134e
    •   Swagger 接口排序同时指定 TagOrder 之后无效 4.8.7.2 ⏱️2023.03.01 #I6IQDI #I6IP66
  • 其他更改

    •   Blazor + WebAPI 脚手架模板,默认添加授权支持 4.8.7.37 ⏱️2023.04.07 #I6OM8O 544f80d
    •   定时任务动态委托作业持久化逻辑,采用不触发持久化操作 4.8.7.36 ⏱️2023.04.06 7bb58b6
    •   多语言中间件 app.UseAppLocalization() 添加 Action<options> 委托参数 4.8.7.30 ⏱️2023.03.31 #I6RUOU
    •   定时任务 Http 作业 HttpMethod 属性拼写错成 HttpMedhod 4.8.7.24 ⏱️2023.03.28 !756
    •   粘土对象 number 类型处理,若含 .double 类型,否则转 long 类型 4.8.7.24 ⏱️2023.03.28 e82e883
    •   视图引擎默认程序集,追加 System.Collections 程序集 4.8.7.16 ⏱️2023.03.18 #I6O3BD
    •   定时任务配置选项 BuilSqlType 属性命为 BuildSqlType 4.8.7.11 ⏱️2023.03.15 92117b8
    •   定时任务查看作业触发器运行记录由保存 10条 改为 5条 4.8.7.7 ⏱️2023.03.07 01d4466
    •   脚手架模板,默认启用主流文件类型 MIME 支持 4.8.7.5 ⏱️2023.03.07 e35cdab
    •   审计日志 LoggingMonitor 返回值泛型字符串显示格式 4.8.7.1 ⏱️2023.02.27 df35201
  • 文档

    •   发布桌面程序 文档
    •   Native 全局静态类文档
    •   ASP.NET 8 集成 文档
    •   .NET7 升级 .NET8 文档
    •   定时任务文档、中间件文档、规范化结果文档、动态 WebAPI 文档、日志记录文档、事件总线文档、虚拟文件系统文档、Sql 高级代理文档、数据库实体文档、任务队列文档、跨域文档、配置选项文档、安全授权、脚手架文档、粘土对象文档、多语言文档
  • 贡献者


v4.8.6(已发布)

版本细节
  • 新特性

    •   审计日志 LoggingMonitor 支持配置序列化属性命名规则 4.8.6.12 ⏱️2023.02.21 #I6GPUP
    •   Swagger 请求授权失败后自动退出授权状态 4.8.6.12 ⏱️2023.02.20 !717
    •   粘土对象支持任何字符作为 JSON/XML4.8.6.9 ⏱️2023.02.19 f99aee8 #note_16329657
    •   动态 WebAPI 自动检查路由是否包含重复参数,如果有自动修正而不是抛异常 4.8.6.5 ⏱️2023.02.17 5f15ea1
查看变化

Furion 4.8.6.5 之前,下列代码会抛出异常The route parameter name 'roleid' appears more than one time in the route template.

原因是生成的路由包含了多个 {roleId}/api/with-class/system/role/deptTree/{roleId}/{roleId}

public class WithClass : IDynamicApiController
{
[HttpGet("system/role/deptTree/{roleId}")] // 过去版本抛异常,Furion 4.8.6.5+ 正常~
public string GetResult2(string roleId)
{
return nameof(Furion);
}
}

新版本 Furion 4.8.6.5+ 修正了该错误,自动移除后面重复的路由参数且不再抛异常,也就是最终生成路由为:/api/with-class/system/role/deptTree/{roleId}

    •   byte[] 类型 MD5 加密/比较重载方法 4.8.6.3 ⏱️2023.02.15 #I6F1NT
    •   动态 WebAPI 支持 [RouteConstraint(":*")] 路由约束 4.8.6.2 ⏱️2023.02.10 #I6E6JA
    •   Swagger 启用登录后配置 CheckUrl 可获取本地存储的 Authorization 请求报文头 4.8.6.2 ⏱️2023.02.10 #I6E3LB
    •   多语言支持 .json 文件配置方式(推荐) 4.8.6 ⏱️2023.02.08 #I6DL71 #I5DXKP
    •   定时任务 IScheduler.[Try]UpdateDetail(builder => {})IScheduler.[Try]UpdateTrigger(triggerId, builder => {}) 重载方法 4.8.6 ⏱️2023.02.08 6e43a54
查看变化
  • 更新作业信息
// 返回 ScheduleResult 类型
var scheduleResult = Scheduler.TryUpdateDetail(jobBuilder =>
{
jobBuilder.SetDescription("~~~");
}, out var jobDetail);

// 无返回值
scheduler.UpdateDetail(jobBuilder =>
{
jobBuilder.SetDescription("~~~");
});
  • 更新作业触发器
// 返回 ScheduleResult 类型
var scheduleResult = scheduler.TryUpdateTrigger("triggerId", triggerBuilder =>
{
triggerBuilder.SetDescription("~~");
}, out var trigger);

// 无返回值
scheduler.UpdateTrigger("triggerId", triggerBuilder =>
{
triggerBuilder.SetDescription("~~");
});
    •   审计日志 LoggingMonitor 支持 [DisplayName] 特性解析和 Title 属性记录 4.8.5.10 ⏱️2023.02.07 #I6DHMF
    •   远程请求配置 SetHttpVersion(version) 配置,可配置 HTTP 请求版本,默认为 1.1 4.8.5.8 ⏱️2023.02.06 #I6D64H
    •   动态 WebAPI 支持更加强大的路由组合功能 4.8.5.7 ⏱️2023.02.03 #I6CLPT
查看变化
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace WebApplication38;

[Route("api/[controller]")]
[Route("api2/[controller]")]
public class Test1Service : IDynamicApiController
{
[HttpGet("test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}

public class Test2Service : IDynamicApiController
{
[HttpGet("/root/test")]
[HttpGet("test")]
[HttpGet(Name = "other-test")]
[HttpGet("template-test", Name = "other-test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}

[Route("api/[controller]")]
[Route("api2/[controller]/second")]
[Route("api3/[controller]/three")]
public class Test3Service : IDynamicApiController
{
[HttpGet]
[HttpGet("get/[action]")]
[HttpPost]
[HttpPost("post/cus-version")]
public string GetVersion()
{
return "1.0.0";
}
}
    •   定时任务 Dashboard 可自定义入口地址 /schedule 4.8.5.6 ⏱️2023.02.02 c5639f5
    •   App.GetServiceLifetime(type) 获取服务注册生命周期类型 4.8.5.3 ⏱️2023.01.31 4a573a8
    •   审计日志 LoggingMonitor 记录 HTTP 响应状态码 4.8.5.2 ⏱️2023.01.30 abb4cbd
    •   定时任务执行上下文 RunId 属性,用于标识单次作业触发器执行 4.8.5.1 ⏱️2023.01.30 1aac470
  • 突破性变化

    •   适配 .NET7.0.3.NET6.0.14 4.8.6.3 ⏱️2023.02.15 eecbf83
    •   动态 WebAPI 生成路由 [HttpMethod(template)] 规则 4.8.5.7 ⏱️2023.02.03 #I6CLPT
查看变化

在过去,TestMethod 生成路由为:/mytest

// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}
}

新版本:TestMethod 生成路由为:/api/class/mytestTestMethod2 生成路由为:/mytest

// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}

[HttpPost("/mytest")]
public void TestMethod2()
{
}
}

也就是新版本如果不需要自动添加前缀,需在前面添加 /,旧版本不需要。

  • 问题修复

    •   规范化结果不带 mini-profiler 版本启动登录 UI 后不能传递 headers 问题 4.8.6.11 ⏱️2023.02.20 #I6G8IR
    •   Serve.Run()#I6G02W 更改导致不配置端口时出现异常无法启动问题 4.8.6.10 ⏱️2023.02.20 #I6G6AR
    •   动态 WebAPI 不支持嵌套继承 [Route] 特性问题 4.8.6.8 ⏱️2023.02.18 #I6CLPT
查看变化

过去版本生成错误重复路由,如:api/system/SystemDictionary/api/system/SystemDictionary/Add,现已修正。

public class WithClass : IDynamicApiController
{
[Route("Add")]
public void Add()
{

}

[Route("Edit")]
public void Edit()
{

}
}

[Route("api/system/SystemDictionary")]
public class SystemService : WithClass
{
public void Some()
{
}
}
    •   Serve.Run(urls: "端口") 设置端口在 .NET6/7 下发布后始终是 80 端口问题 4.8.6.6 ⏱️2023.02.18 #I6G02W
    •   粘土对象不支持 中文 作为 JSON/XML 键问题 4.8.6.6 ⏱️2023.02.18 4961e01
    •   远程请求代理模式配置了 WithEncodeUrl = false 无效问题 4.8.6.4 ⏱️2023.02.16 89639ba
    •   动态 WebAPI 自定义 [HttpMethod(template)] 之后生成错误路由 4.8.6.1 ⏱️2023.02.08 59fe53b
    •   由于 #I6D64H 导致远程请求出现 Specified method is not supported. 问题 4.8.5.9 ⏱️2023.02.07 #I6DEEE #I6D64H
    •   优化远程请求 ReadAsStringAsync 底层方法,尝试修复 Error while copying content to a stream. 错误 4.8.5.8 ⏱️2023.02.06 #I6D64H
    •   规范化结果不支持 OData 协议控制器 4.8.5.5 ⏱️2023.02.01 !571
    •   启用 Swagger 登录功能之后不能触发响应拦截器 4.8.5.5 ⏱️2023.02.01 #I6C9A2 !702 !703
    •   在数据库日志的 IDatabaseLoggingWriter 实现类中依赖注入 ILogger<> 导致死循环 4.8.5.4 ⏱️2023.02.01 #I6C6QU
    •   Furion.Xunit/Furion.Pure.Xunit 单元测试依赖注入单例服务时不是同一实例问题 4.8.5.3 ⏱️2023.01.31 305511e
    •   数据库日志提供程序在应用程序终止时出现空异常问题 4.8.5 ⏱️2023.01.28 #I6AZ8Y
    •   实体拓展方式操作数据库出现空异常问题 4.8.5 ⏱️2023.01.28 #I6AXU6
  • 其他更改

    •   脱敏处理 sensitive-words.txt 嵌入文件支持 UTF8 BOM 编码,感谢 @man119 4.8.6.7 ⏱️2023.02.18 #I6G1JN
    •   Serve.Run() 迷你主机默认添加 JSON 中文乱码处理 4.8.6.3 ⏱️2023.02.15 86b5f9f
  • 文档

    •   多语言 .json 配置方式文档
    •   日志文档、定时任务文档、动态 WebAPI 文档,规范化结果文档,App 静态类文档,Oops 静态类文档、虚拟文件系统文档 !704,远程请求文档,序列化文档、入门文档、脱敏模块文档
  • 贡献者


v4.8.4(已发布,全新定时任务)

更好的 Furion,更好的自己

在过去两年,实现 Furion 从无到有,编写文档已逾三百万字,过程心酸开源人自知。

这一路日夜兼程,嘲讽批评常伴眼耳,即便辛苦无奈、想过放弃,但为了那微不足道的成就感依然努力着。

当然,也收获了不少... 越来越多拥趸者,越发精湛技术能力,更高层次思维模式,还有许多跨界跨行朋友。

在 《开源指北》中,我曾说道:“开源如同人的脸,好坏一面便知,缺点可能会受到嘲讽批评,优点也会收获赞扬尊重。别担心,他们正在塑造更好的你。”

.NET 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力。所以,这一次重新起航,重塑 Furion 重塑自己。也许未来在某个 IT 圈但凡有人谈起 .NET 还能瞟到 Furion 的身影。

版本细节
  • 新特性

    •   🎉 全新的定时任务模块 4.8.0查看源码
    •   🎉 全新的 Cron 表达式模块 4.8.0查看源码
    •   🎉 全新的任务队列模块 4.8.3查看源码
    •   视图引擎支持无命名空间的强类型 4.8.4.16 ⏱️2023.01.15 #I6ABN3 #I6A7SI 076bb17
    •   视图引擎支持匿名类型模型带集合类型属性 @foreach 遍历 4.8.4.15 ⏱️2023.01.13 #I6A7SI
    •   Swagger 支持复制路由地址功能 4.8.4.13 ⏱️2023.01.11 #I5VNJI
    •   动态 WebAPI 方法支持通过 [ActionName(名称)][HttpMethod(Name=名称)] 指定路由名称 4.8.4.12 ⏱️2023.01.10 #I69AOJ f699540
    •   BadPageResult.Status401Unauthorized 等常见状态码 401,403,404,500 静态属性 4.8.4.11 ⏱️2023.01.09 #I69KQF
    •   crontab.GetSleepTimeSpan(baseTime) 实例方法 4.8.4.10 ⏱️2023.01.09 #I69HM4
    •   Enqueue/EnqueueAsync 支持 Cron 表达式 实例重载方法 4.8.4.10 ⏱️2023.01.09 #I69HM4
    •   *.bcmap.properties 文件类型 MIME 支持 4.8.4.9 ⏱️2023.01.06 !694
    •   定时任务 Dashboard 查看作业触发器最近运行记录功能 4.8.4.3 ⏱️2023.01.03 e7d24d8
    •   定时任务作业触发器 trigger.GetTimelines() 获取最近 10 条运行记录列表 4.8.4.3 ⏱️2023.01.03 e7d24d8
    •   定时任务 Dashboard 看板 4.8.4 ⏱️2022.12.30 d3f9669
    •   定时任务 IScheduler.GetEnumerable() 方法,可将作业计划转换成可枚举字典 4.8.4 ⏱️2022.12.30 4d5235c
    •   L.SetCurrentUICulture(culture)L.GetCurrentUICulture() 静态方法,可在运行时动态修改当前线程区域性 4.8.3.10 ⏱️2022.12.23 #I66JWA
    •   L.SetCulture(culture, immediately) 方法重载,可配置运行时修改多语言立即有效 4.8.3.10 ⏱️2022.12.23 #I66JWA
    •   定时任务配置选项 options.JobDetail.LogEnabled 配置,可自动输出执行日志 4.8.3.7 ⏱️2022.12.14 58d2c20
    •   ValidationTypes 更多常见验证格式(手机机身码类型统一社会信用代码GUID/UUIDbase644.8.3.6 ⏱️2022.12.13 3680d7a
    •   定时任务 IScheduler 对象每次操作后自动刷新和提供手动刷新 Reload() 方法 4.8.3.3 ⏱️2022.12.09 #I65EQ1
    •   定时任务间隔分钟作业触发器 Triggers.PeriodMinutes(5)[PeriodMinutes(5)] 特性 4.8.2.8 ⏱️2022.12.01 8e1f06f
    •   定时任务工作日作业触发器 Triggers.Workday()[Workday] 特性 4.8.2.6 ⏱️2022.11.30 28b2d20
    •   定时任务作业校对功能,可对误差进行校正 4.8.2.6 ⏱️2022.11.30 f725a25
    •   Crontab.ParseAt(..) 静态方法 4.8.2.6 ⏱️2022.11.30 035cc23
    •   Crontab 所有 Macro At 静态方法 4.8.2.6 ⏱️2022.11.30 a15b69d
    •   Crontab.Workday 表示周一至周五的 Macro 静态属性 4.8.2.6 ⏱️2022.11.30 a15b69d
    •   定时任务 Triggers 所有带 AtCron 表达式触发器构建器及特性 4.8.2.5 ⏱️2022.11.29 #I63PLR
    •   App.GetThreadId()App.GetTraceId() 获取线程 Id 和请求 TraceId 4.8.2.4 ⏱️2022.11.29 910fc1f
    •   App.GetExecutionTime(() => { /*Your Code*/ }) 获取代码执行耗时 4.8.2.4 ⏱️2022.11.29 5ab4b19
    •   定时任务批量添加 SchedulerBuilder 作业功能 4.8.2.4 ⏱️2022.11.29 5faa67b
    •   定时任务 BuildSqlType 配置,可设置生成不同数据库类型的 SQL 语句 4.8.2.3 ⏱️2022.11.29 293f9bc !675
    •   JobDetailTrigger 自定义 ConvertToSQL 输出 SQL 配置 4.8.2 ⏱️2022.11.27 0bb9d8f
    •   动态作业处理程序委托支持 4.8.1.8 ⏱️2022.11.27 e02266c
    •   作业触发器 ResetOnlyOnce 属性,支持只运行一次的作业重新启动服务重复执行 4.8.1.5 ⏱️2022.11.25 a8be728
    •   事件总线支持简单的 Order 编排规则 4.8.0 833c0d4
    •   远程请求代理模式对于基元类型参数支持自动获取参数名 4.8.0 #I60OT6
    •   动态 WebAPI 自动识别方法的接口参数是否是服务,如果是自动添加 [FromServices] 特性 4.8.0 fae60a9
    •   远程请求 [QueryString] 特性添加时间格式化 Format 属性 4.8.1.2 !670
    •   Serve.Run 模式的 .ConfigureServices 方法 4.8.0 023391b
    •   Serve.RunGeneric 通用泛型主机方法 4.8.0 6865f3d
    •   Serve.Run()additional 参数 4.8.0 023391b
查看变化

极速入门现在可以便捷注册服务,写测试例子的时候非常有用,无需编写 Startup.cs

Serve.Run(additional: services =>
{
services.AddRemoteRequest();
});

// 通用泛型主机方式
Serve.RunGeneric(additional: services =>
{
services.AddRemoteRequest();
});

// 还可以省去 additional
Serve.Run(services =>
{
services.AddRemoteRequest();
});

// 通用泛型主机方式
Serve.RunGeneric(services =>
{
services.AddRemoteRequest();
});
    •   Serve.Run 主机返回值 IHost 4.8.0 #I61XHV
查看变化

Winfom/WPF 应用程序中,我们希望关闭窗体或退出应用程序时,能够关闭 Serve 主机:

using Microsoft.Extensions.Hosting;

public partial class App : Application
{
private readonly IHost _host;
public App()
{
_host = Serve.Run(silence: true);
}

protected override void OnExit(ExitEventArgs e)
{
_host.StopAsync();
_host.Dispose();

base.OnExit(e);
}
}
    •   日志 JSON 自动美化格式化器 LoggerFormatter.JsonIndented 4.8.0 7b9268c
    •   LoggingMonitorJsonIndented 配置,可配置是否美化 JSON 4.8.0 7b9268c
查看变化

默认情况下,配置输出 JSON 格式化 LoggerFormatter.Json 会浓缩到一行显示。

新版本支持 LoggerFormatter.JsonIndented 美化 JSON 配置:

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// 文件日志
services.AddFileLogging("mytemplate.log", options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// 数据库日志
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// LoggingMonitor 日志
services.AddMonitorLogging(options =>
{
options.JsonIndented = true;
});
    •   日志模块是否输出 TraceId,同一个请求的日志 TraceId 一致 4.8.1.3 #I62VGG
查看变化

在生产环境中,日志的输出是非常频繁的,但是很难从日志文件中判断哪些日志是属于同一个请求输出的,所以新增 WithTraceId 配置。

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.WithTraceId = true;
});

// 文件日志
services.AddFileLogging("mytemplate.log", options =>
{
options.WithTraceId = true;
});

// 数据库日志
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.WithTraceId = true;
});

输出日志如下:

info: 2022-11-24 14:34:55.1717549 +08:00 星期四 L System.Logging.EventBusService[0] #1
EventBus Hosted Service is running.
info: 2022-11-24 14:34:55.2504015 +08:00 星期四 L System.Logging.ScheduleService[0] #1
Schedule Hosted Service is running.
info: 2022-11-24 14:34:56.4280796 +08:00 星期四 L Microsoft.Hosting.Lifetime[14] #1
Now listening on: https://localhost:5001
info: 2022-11-24 14:34:56.4331170 +08:00 星期四 L Microsoft.Hosting.Lifetime[14] #1
Now listening on: http://localhost:5000
info: 2022-11-24 14:34:56.4384567 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Application started. Press Ctrl+C to shut down.
info: 2022-11-24 14:34:56.4408766 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Hosting environment: Development
info: 2022-11-24 14:34:56.4427659 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Content root path: D:\Workplaces\OpenSources\Furion\samples\Furion.Web.Entry
info: 2022-11-24 14:35:06.8507338 +08:00 Thursday L Furion.Application.TestLoggerServices[0] #17 '00-48df9ac5c8280de2f301faa44a23a10c-b75678d9f3883b0b-00'
我是一个日志 20
info: 2022-11-24 14:35:16.0373384 +08:00 星期四 L Furion.Application.TestLoggerServices[0] #17 '00-ff4fb15d6ff41a0411784e66400f0dfd-962bc25eff788b25-00'
我是一个日志 20
  • 突破性变化

    •   旧版本定时任务为 弃用 状态(2022 年 12 月 31 日彻底删除),如需取消警告在 .csproj 中添加 <NoWarn>0618</NoWarn> 4.8.0 旧版本文档
    •   JSON 静态类所有方法的 serviceProvider 参数(无破坏) 4.8.0 962fb16
    •   Http 静态类所有方法的 serviceProvider 参数(无破坏) 4.8.0 962fb16
    •   定时任务底层所有代码,日志,注释,文档 4.8.1.10 ⏱️2022.12.05
  • 问题修复

    •   定时任务 StartAll 出现个别作业显示 无触发时间 的状态 4.8.4.14 ⏱️2023.01.12 #I6A08X
    •   动态 WebAPI 配置 [Consumes] 特性后 Swagger 不显示问题 4.8.4.12 ⏱️2023.01.10 daf25f8
    •   定时任务停止作业触发器后运行记录不能写入最新记录问题 4.8.4.8 ⏱️2023.01.05 d4c553f
    •   数据库日志注册在一些特殊情况下丢失日志上下文问题 4.8.4.6 ⏱️2023.01.04 #I68PDF
    •   定时任务使用 Furion.Pure 包访问 Dashboard 出现 404 问题 4.8.4.2 ⏱️2023.01.02 21977b7
    •   在类中贴 [SuppressMonitor] 特性但 LoggingMonitor 依然输出问题 4.8.4 ⏱️2022.12.30 #I6882I
    •   远程请求配置 WithEncodeUrl(false)application/x-www-form-urlencoded 请求类型无效 4.8.4 ⏱️2022.12.30 #I682DX
    •   LoggingMonitor 序列化 IQueryable<>OData 返回值类型出现死循环问题 4.8.3.4 ⏱️2022.12.10 7e8c9d0
    •   定时任务通过 scheduler.RemoveTrigger(triggerId) 报异常问题 4.8.3.3 ⏱️2022.12.09 #I65EQ1
    •   定时任务作业触发器配置了 EndTimeStartTime 之后 Status 没有对应上 4.8.3.1 ⏱️2022.12.09 52a5506
    •   定时任务通过 scheduler.AddTrigger(triggerBuilder) 无效的问题 4.8.3.1 ⏱️2022.12.09 #I65EQ1
    •   作业拥有多个触发器时暂停作业后依然存在个别未暂停的清空(并发问题) 4.8.2.12 ⏱️2022.12.07 #I655W9
    •   通过 Ctrl + C 终止应用程序后获取 TraceId 出现对象已释放异常 4.8.1.12 ⏱️2022.12.07 55c3e49
    •   cli.ps1 脚本不支持 EFCore 7.0 问题 4.8.1.12 ⏱️2022.12.07 !676
    •   EFCore 实体监听器 IEntityChangedListener 问题 4.8.1.7 ⏱️2022.11.26 #I61CTI
    •   定时任务生成的 SQL 语句不支持 MySQL 问题 4.8.1.7 ⏱️2022.11.26 #I638ZC
    •   运行时启动/暂停作业无效问题 4.8.1.6 ⏱️2022.11.25 #I6368M
    •   作业触发器不符合下一次执行规律但 NextRunTime 不为 null 情况 4.8.1.5 ⏱️2022.11.25 a8be728
    •  .NET6/7 降级回 .NET5 找不到 .AddDateOnlyConverters().AddTimeOnlyConverters() 拓展方法问题 4.8.0 cdddf8d
    •   Retry.InvokeAsync 方法如果不传入 fallbackPolicy 参数报空异常问题 4.8.0 21af847
    •   动态 WebAPI 不支持在 .NET7 不声明 [FromServices] 自动注入问题 4.8.0 #I62HP1
    •   远程请求 GetAsStreamAsync()System.InvalidOperationException: Response Content-Length mismatch 异常问题 4.8.1 #I62QY4
    •   LoggingMonitor 配置 WriteFilter 不起作用问题 4.8.1.2 #I62P52 90bcfda
    •   EFCore 个别关系型数据库 PostgreSQL/SqlServer/MySql 出现短暂不能连接问题 4.8.1.3 2c530ef
    •   日志模块因 v4.8.0+ 版本导致写入数据库日志空异常问题 4.8.2.1 ⏱️2022.11.28 8d9d72b
  • 其他更改

    •   定时任务调度器时间精度,控制持续执行一年误差在 100ms 以内 4.8.2.9 ⏱️2022.12.01 334d089
    •   定时任务作业计划工厂 GetNextRunJobs() 方法逻辑 4.8.2.7 ⏱️2022.11.30 #I63VS2
    •   LoggingMonitor 解析授权逻辑,如果接口未授权则不打印授权信息 4.8.2.1 ⏱️2022.11.28 #I63D2E
  • 文档

    •   新版本定时任务文档
    •   Cron 表达式解析文档
    •   任务队列文档
    •   Schedular 全局静态类文档
    •   TaskQueued 全局静态类文档
    •   作业执行器实现超时文档 4.8.3.8 ⏱️2022.12.20
    •   作业触发器 ResetOnlyOnce 文档 4.8.1.5 ⏱️2022.11.25 a8be728
    •   通过 Roslyn 动态编译代码创建 IJob 类型文档 4.8.1.5 ⏱️2022.11.25 2c5e5be
    •   自定义 JobDetailTrigger 输出 SQL 文档 4.8.2 ⏱️2022.11.27 0bb9d8f
    •   远程请求 [QueryString] 配置时间类型 Format 格式化文档 4.8.1.2 ⏱️2022.11.25 !673
    •   Serve.Run() 入门文档文档、安全授权文档、前端接口代理文档、事件总线文档、日志文档、Worker Service 文档、数据库实体触发器文档、App 静态类文档、包管理工具文档

v4.7.9(已发布,.NET7)

.NET7 发布

🚀🎉🔥 2022 年 11 月 08 日,微软发布了 .NET7 首个正式版。

Furion 第一时间完成了适配,v4 版本开始一套代码支持 .NET5-.NET7/N,支持所有 Furion 版本升级

版本细节
  • 新特性

    •   日志模块时间格式化默认输出 毫秒 部分,针对并发比较高的场景 4.7.0 c0dc36c
    •   写入数据库日志死循环输出检测机制 4.7.0 30dea0c
    •   LoggingMonitor 输出 系统信息.NET 架构基础框架 4.7.1 aeda902
    •   远程请求 .SetQueries(obj, ignoreNullValue) 重载方法 4.7.3 #I5Z8KC
    •   远程请求 .GetCookies().GetSetCookies() 拓展方法 4.7.5 #I5ZY1L
    •   事件总线 .ReplaceStorerOrFallback 自定义事件源存储器方法,可在自定义初始失败时回退到默认值 4.7.6 #I602NU
    •   LoggingMonitor 输出 启动信息Cookies请求端源 信息 4.7.7 3037b04
    •   JSON 序列化 DateOnlyTimeOnly 类型转换器:.AddDateOnlyConverters().AddTimeOnlyConverters() 4.7.9 !657 47a5fcb
    •   HttpContext.ReadBodyContentAsync() 拓展方法重复读取 Body 内容 4.7.9 #I60IYU
  • 突破性变化

    •   所有脚手架支持 -f 指定 .NET 版本 4.7.6 #I603AZ
查看变化
# 创建 .NET5 版本
dotnet new furionapi -n 项目名称 -f net5

# 创建 .NET6 版本
dotnet new furionapi -n 项目名称 -f net6

# 创建 .NET7 版本
dotnet new furionapi -n 项目名称 -f net7
    •   适配 .NET 6.0.11.NET 7 4.7.5 7df3195
    •   所有脚手架至 .NET 7 4.7.5 7df3195
    •   LogContext 类型的所有方法至 Furion.Logging 命名空间下,解决空异常问题 4.7.3 #I5YOT3
查看变化

由:

var value = logContext.Get("Key");  // 过去如果 logContext == null 报错

改为:

using Furion.Logging;

var value = logContext.Get("Key"); // 新版本不会报错,且 value = null
    •   旧版本定时任务为 弃用 状态(一周内发布新版),如需取消警告在 .csproj 中添加 <NoWarn>0618</NoWarn> 4.7.9 0ff3ac0
  • 问题修复

    •   生成 JWT Token 时间戳和自动刷新逻辑在高并发下检查有效性不够精确问题,原因是时间戳丢掉了毫秒部分 4.7.0 3c0c017
    •   IDatabaseLoggingWriter 实现类中输出日志导致死循环问题 4.7.0 30dea0c
    •   规范化结果 OnResponseStatusCodes 方法在 Response 已完成写入时设置出现异常问题 4.7.2 #I5YBHL
    •   L.SetCulture("zh-CN");Response 已完成写入时设置出现异常问题 4.7.2 #I5YBHL
    •   动态 WebAPI 在类上配置 [Route] 特性且包含 [action] 模板导致生成错误接口路径 4.7.2 #I5YEZQ
    •   启用二级虚拟目录 AppSettings:VirtualPath 导致 swaggerminiprofile 加载失败 4.7.3 #I5Z8RM
    •   LoggingMonitor 监听带有 [FromServices] 的方法参数或接口类型参数出错 4.7.7 3037b04
    •   HttpRequest 通过 .ReadBodyContentAsync() 读取不到 Body 问题 4.7.9 #I60IYU
  • 其他更改

    •   JSON Schema 配置,新增日志更多参数提醒 4.7.0 74bee56
    •   日志记录时间格式默认输出带 7位 的毫秒值 4.7.1 aeda902
    •   所有脚手架默认启用 单文件/独立部署 配置 4.7.7 1277f53
  • 文档

    •   IIS 回收问题解决方案文档
    •   远程请求获取 Cookies 文档
    •   LoggingMonitor 写入数据库文档
    •   JSON 序列化 DateOnlyTimeOnly 类型处理文档
    •   HttpContext 读取 Body 内容文档
    •   PM2 配置文件 json 部署文档
    •   日志记录文档、定时任务文档、远程请求文档、脚手架文档
  • 特别贡献


v4.6.9(已发布)

版本细节
  • 新特性

    •   LoggingMonitor 支持 FileResult 类型监听 4.6.0 bf9c0b1
    •   LogMessage 结构 UseUtcTimestamp 字段,解释日志记录时间格式是 UTC 还是 LOCAL 时间 4.6.1 aab0371
    •   事件总线模块重试失败后支持回调 4.6.1 #I5UVMV
    •   LoggingMonitor 支持序列化忽略指定属性名或属性类型 4.6.1 81c6343
    •   long 序列化丢精度的 JsonConvert 内置转换器,.AddLongTypeConverters() 4.6.5 #I5VJHC aded58d
    •   app.EnableBuffering() 拓展,解决 Request.Body 不能重复读问题 4.6.5 aded58d
    •   支持特别接口使用特定的序列化规则 4.6.6 797b0bf
    •   LoggingMonitor 自动解析 JWT 时间戳为时间格式 4.6.8 9e31b0b
  • 突破性变化

    •   适配 .NET 6.0.10.NET 7 RC2 4.6.2 6bb2fad
    •   内置 Microsoft.AspNetCore.Mvc.NewtonsoftJson 拓展,原因是太多人使用了 4.6.5 aded58d
    •   "some log".SetCategory(name) 拓展方法 4.6.0 ec4838c
    •   DateOnlyJsonConverterDateOnlyOffsetJsonConverter 处理 4.6.5 aded58d
    •   事件总线触发处理程序的逻辑,由过去的 foreach 改为 Parallel.ForEach,吞吐量提升近 4 倍 4.6.4 7384c9c
    •   .AddDateFormatString() 名称为 .AddDateTimeTypeConverters() 4.6.5 aded58d
    •   重构日志模块设置上下文数据功能 4.6.0 1c198ee
查看变化

由于过去版本设置日志上下文有多线程异常和堆内存溢出风险,所以重新设计了日志上下文的写法。

由:

_logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10))
.LogInformation("我是一个日志 {id}", 20);

改为:

using (var scope = _logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10)))
{
_logger.LogInformation("我是一个日志 {id}", 20);
}

// 也可以简写
using var scope = _logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10));
_logger.LogInformation("我是一个日志 {id}", 20);
    •   远程请求 .SetTimeout[Timeout] 配置方法,采用全局统一配置 4.6.4 7384c9c
查看变化

默认情况下,HttpClient 请求超时时间为 100秒,可根据实际情况进行设置:

// 配置默认 HttpClient
options.AddHttpClient(string.Empty, c =>
{
c.Timeout = TimeSpan.FromMinutes(2);
});

// 配置特定客户端
options.AddHttpClient("github", c =>
{
c.Timeout = TimeSpan.FromMinutes(2);
});
  • 问题修复

    •   4.5.9+ 版本新增的 IncludeScopes 配置导致日志上下文失效 4.6.0 4a76841
    •   多个 sql 共用 DbParameters 出现冲突问题 4.6.0 #I5UO2H
    •   高频率写入日志导致堆内存溢出的异常问题 4.6.0 #I5UJRS
    •   框架内部所有使用 .CreateLogger 创建的日志对象无法应用上下文问题 4.6.0 ec4838c
    •   远程请求不能在 Worker Serivce 中进行构造函数注入,原因是注册为 Scope 范围作用域 4.6.3 974f835
    •   个别服务器的 SQL Server 不支持 TLS 1.2 协议问题 4.6.3 974f835
    •   .ToDictionary() 拓展不支持 JObject 类型问题 4.6.5 #I5VJHC a11bf8d
    •   LoggingMonitor 处理 long 类型丢精度问题 4.6.5 #I5VJHC aded58d
    •   动态 WebAPIclass 类型上贴 [ApiDescriptionSettings(false)] 导致接口 404 问题 4.6.7 #I5WQ18
    •   超高频率下发送事件总线消息,但是 GC 来不及回收导致内存和 CPU 爆掉问题 4.6.8 dbc7935
    •   JWT 模块自动刷新 Token 达到临界值时导致自动刷新失败,并返回错误的 401 状态码 4.6.8 #I5WXHZ
    •   自动生成 vue/react/angular 客户端工具库错误处理 Token 问题 4.6.8 #I5WXHZ
    •   远程请求没有正确处理 数组和集合 类型的 url 参数 4.6.9 #I5XIQ4
    •   自定义 Tenant 实体且包含 TenantId 属性且没有继承 EntityBase/Entity 基类出现 The entity type 'Tenant' requires a primary key to be defined 4.6.9 #I4UM3E
  • 其他更改

    •   LoggingMonitor 返回值类型是 泛型 时获取 FullName 带程序集签名问题 4.6.2 f0aaec6
    •   优化远程请求性能,添加复用池的机制,避免频繁销毁创建 4.6.4 7384c9c
  • 文档

    •   远程请求设置客户端生命周期配置文档和新超时配置文档
    •   JSON 序列化处理 long 类型说明文档
    •   JSON 反序列化 DateTimeOffset 类型个别格式出错问题解决方案文档
    •   Worker Service 实现 串行 操作文档
    •   关闭 .NET Core 底层日志和远程请求日志文档
    •   规范化结果支持特定接口配置独立序列化配置文档
    •   日志记录文档、事件总线文档、数据库入门文档、JSON 序列化文档、远程请求文档、安全授权文档、生成前端请求代理文档

v4.5.9(已发布)

版本细节
  • 新特性

    •   .AddConsoleFormatter() 拓展简化控制台日志模板配置 4.5.0 #I5TCMO
    •   控制台和文件日志时间默认显示 星期几 4.5.1 #I5TKL5
    •   控制台和文件日志支持配置 options.DateFormat 日期格式化 4.5.1 #I5TKL5
    •   控制台日志带颜色输出,比如高亮 日志级别 4.5.1 #I5TKL5
    •   控制台格式化配置 options.WriteHandler 完全自定义配置 4.5.2 7fb3036
    •   日志输出 JSON 格式化配置 4.5.2 #I5TWC1 #I5OUT1
    •   数据库日志写入独立日志模板配置、独立日期格式配置 4.5.2 #I5TWC1
    •   LogMessage 结构类 LogDateTimeThreadIdState 属性 4.5.2 #I5TWC1
    •   LoggingMonitor 可配置 JsonWriterOptions 属性 4.5.4 #I5U375
    •   Log.ScopeContext"some log".ScopeContext 拓展 4.5.4 8129693
    •   HttpContext.SetTokensOfResponseHeaders 拓展 4.5.7 3775e65
    •   新增远程请求支持 Stream 文件格式上传 4.5.8 #I5UF3I
    •   日志模块可配置是否启用上下文功能 IncludeScopes 属性 4.5.9 #I5UJRS
    •   LoggingMonitor 日志筛选 WriteFilter 配置 4.5.9 6f06f12
  • 突破性变化

    •   😊 Furion 框架文档地址为 http://furion.baiqian.ltd 4.5.4 2e3d80e
    •   LoggingMonitor 底层逻辑,移除原来的 .ScopeContext 存储监听信息设计 4.5.2 #I5TWC1
    •   主机未启动时构建服务的操作权限,此操作会导致内存激增,受影响方法: App.GetOptions 系列和 App.GetServiceScoped.Create 4.5.4 #I5U0A4 8129693
查看变化

近期发现许多开发者在主机还未启动时解析服务,这是非常不正确的行为,会导致启动时内存激增甚至溢出,常见的错误有:

  • 在启动的时候通过 Scoped.Create 创建作用域
  • 在启动的时候通过 App.GetOptions 获取选项对象
  • 在启动的时候通过 App.GetService<T> 解析服务

正确的做法是,启动的时候禁止使用 Scoped.CreateApp.GetService<T>

如需启动时获取配置应该通过:App.GetConfig<TOptions>("配置节点", true) 替代 App.GetOptions<TOptions>()

  • 问题修复

    •   字符串日志拓展带泛型方法不能正确显示 CategoryName 日志类别 4.5.0 #I5TBKL
    •   控制台日志设置了 .ScopeContext 无效问题 4.5.2 7fb3036
    •   LoggingMonitor 同时配置了局部和全局日志监听触发两次问题 4.5.2 a1a97e8
    •   v4.4.8+ 版本更新导致远程请求在个别情况下出现并发问题 4.5.2 #I5TWL3
    •   LoggingMonitor 配置了 ReturnValueThreshold 之后 Json 被截断引发有效性检测异常 4.5.4 #I5U375
    •   LoggingMonitor 不支持 DataTableDataSetTuple 等类型问题 4.5.5 #I5U3VO
    •   v4.5.2+ 版本升级后出现启动时使用 App.GetOptons<TOptions> 异常问题 4.5.6 #I5U4OC f9a6587
    •   app.UseInject(action) 导致死循环 4.5.7 !608
    •   LoggingMonitor 报空引用异常问题 4.5.8 #I5UGCA !610
    •   并发情况下设置日志上下文出现偶然性空引用问题 4.5.9 #I5UJRS
  • 其他更改

    •   文件日志默认模板,默认对日志时间进行格式化并显示星期几 4.5.1 #I5TKL5
    •   脚手架代码,默认启用 services.AddConsoleFormatter() 4.5.1 #I5TLI6
    •   Serve.Run() 代码,默认启用 services.AddConsoleFormatter() 4.5.1 #I5TLI6
    •   减少 MiniProfile 不必要的监听,只在 Swagger 页面请求才监听 4.5.7 697ef51
    •   日志模块所有日志时间默认为 24小时制,过去是 12小时制 4.5.9 !612
  • 文档

    •   选项监听出现触发多次的解决方案 #I5T9PR
    •   日志记录文档、动态 WebAPI 文档、选项文档、HttpContext 文档、远程请求文档
  • 本期亮点

    1. 支持日志配置 JSON 格式化输出
查看变化
// 控制台
services.AddConsoleFormatter(options =>
{
options.MessageFormat = LoggerFormatter.Json;
});

// 文件
services.AddFileLogging("mytemplate.log", options =>
{
options.MessageFormat = LoggerFormatter.Json;
});

// 数据库
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.MessageFormat = LoggerFormatter.Json;
});
    1. 支持 LoggingMonitor 输出 JSON 格式
查看变化
  1. 全局/局部启用 Json 输出配置
// 全局
services.AddMonitorLogging(options =>
{
options.JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson;
});

// 局部
[LoggingMonitor(JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson)]
关于 JsonBehavior

只有设置为 JsonBehavior.OnlyJson 时才不会输出美观的日志。

  1. 写入存储介质
using Furion.Logging;

namespace Your.Core;

public class DatabaseLoggingWriter : IDatabaseLoggingWriter
{
// 支持构造函数注入任何实例,会自动释放任何服务,比如注入 IRepository,或者 SqlSugarClient
public DatabaseLoggingWriter()
{
}

public void Write(LogMessage logMsg, bool flush)
{
// 如果 JsonBehavior 配置为 OnlyJson 或者 All,那么 Context 就包含 loggingMonitor 的值
// 如果 JsonBehavior 配置为 OnlyJson,那么可直接通过 logMsg.Message 获取结果就是 json 格式
if (logMsg.LogName == "System.Logging.LoggingMonitor")
{
var jsonString = logMsg.Context.Get("loggingMonitor");
}

// 这里写你任何插入数据库的操作,无需 try catch
}
}

Json 输出格式如下:

{
"controllerName": "test-logger",
"controllerTypeName": "TestLoggerServices",
"actionName": "person",
"actionTypeName": "GetPerson",
"areaName": null,
"displayName": "Furion.Application.TestLoggerServices.GetPerson (Furion.Application)",
"localIPv4": "0.0.0.1",
"remoteIPv4": "0.0.0.1",
"httpMethod": "GET",
"requestUrl": "https://localhost:5001/api/test-logger/person/2",
"refererUrl": "https://localhost:5001/api/index.html?urls.primaryName=数据库操作演示",
"environment": "Development",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.53",
"requestHeaderAuthorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIkFjY291bnQiOiJhZG1pbiIsImlhdCI6MTY2NDQ1MDUwNSwibmJmIjoxNjY0NDUwNTA1LCJleHAiOjE2NjQ0NTE3MDUsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.-xocNcDQGoXClceoVU5QAHIkTcOZ7ZXo0hEbzghDfFI",
"timeOperationElapsedMilliseconds": 55,
"authorizationClaims": [
{
"type": "UserId",
"valueType": "integer",
"value": "1"
},
{
"type": "Account",
"valueType": "string",
"value": "admin"
},
{
"type": "iat",
"valueType": "integer",
"value": "1664450505"
},
{
"type": "nbf",
"valueType": "integer",
"value": "1664450505"
},
{
"type": "exp",
"valueType": "integer",
"value": "1664451705"
},
{
"type": "iss",
"valueType": "string",
"value": "dotnetchina"
},
{
"type": "aud",
"valueType": "string",
"value": "powerby Furion"
}
],
"parameters": [
{
"name": "id",
"type": "System.Int32",
"value": 2
}
],
"returnInformation": {
"type": "Furion.UnifyResult.RESTfulResult`1[[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"actType": "Furion.Application.Persons.PersonDto",
"value": {
"StatusCode": 200,
"Data": {
"Id": 2,
"Name": null,
"Age": 0,
"Address": null,
"PhoneNumber": null,
"QQ": null,
"CreatedTime": "0001-01-01T00:00:00+00:00",
"Childrens": null,
"Posts": null
},
"Succeeded": true,
"Errors": null,
"Extras": null,
"Timestamp": 1664450517341
}
},
"exception": {
"type": "System.DivideByZeroException",
"message": "Attempted to divide by zero.",
"stackTrace": " at Furion.Application.TestLoggerServices.测试日志监听8(Int32 id) in D:\\Workplaces\\OpenSources\\Furion\\samples\\Furion.Application\\TestLoggerServices.cs:line 78\r\n at lambda_method103(Closure , Object , Object[] )\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)"
},
"validation": {
"errorCode": null,
"originErrorCode": null,
"message": "出错了啊。。。。"
}
}
    1. 支持远程请求上传文件 Stream
查看变化
  1. 单文件上传
  • 字符串方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await "https://localhost:44316/api/test-module/upload-file"
.SetContentType("multipart/form-data")
.SetFiles(HttpFile.Create("file", fileStream, "image.png")).PostAsync();

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

await fileStream.DisposeAsync();
  • 代理方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await _http.TestSingleFileProxyAsync(HttpFile.Create("file", fileStream, "image.png"));
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  1. 多文件上传
  • 字符串方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await "https://localhost:44316/api/test-module/upload-muliti-file"
.SetContentType("multipart/form-data")
.SetFiles(HttpFile.CreateMultiple("files", (fileStream, "image1.png"), (fileStream, "image2.png"))).PostAsync();
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  • 代理方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await _http.TestMultiFileProxyAsync(HttpFile.CreateMultiple("files", (fileStream, "image1.png"), (fileStream, "image2.png")));
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  1. 还支持 BytesStream 混合
var fileStream = new FileStream("image.png", FileMode.Open);
var bytes = File.ReadAllBytes("image.png");

var httpFile = new HttpFile
{
Name = name,
Bytes = bytes,
FileStream = fileStream,
FileName = fileName
};

var result = await "https://localhost:44316/api/test-module/upload-file"
.SetContentType("multipart/form-data")
.SetFiles(httpFile).PostAsync();

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

await fileStream.DisposeAsync();
    1. LoggingMonitor 全局过滤
查看变化
services.AddMonitorLogging(options =>
{
options.WriteFilter = (context) =>
{
// 获取控制器/操作描述器
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;

// 你的逻辑....,不需要拦截返回 false,否则 true

return true;
};
});

v4.4.9(已发布)

版本细节
  • 新特性

    •   友好异常可控制是否输出错误日志配置 LogError: true 4.4.0 #I5PKJH
    •   DateOnlyJsonConverterDateOnlyOffsetJsonConverter 序列化转换器 !565
    •   事件总线 LogEnabled 配置,可控制是否输出服务日志 #I5QLY5
    •   可实现任何多套规范化结果功能,支持特定控制器,特定方法 #I5QZ37
    •   ILoggerFactory 日志工厂动态批量添加文件日志拓展 #I5R9PO
    •   App.GetCommandLineConfiguration(args) 解析命令行参数静态方法 803542c
    •   Sql 代理支持返回受影响行数 #I5REJ9
    •   任意自定义日志文件名支持滚动日志删除功能 #I5RFBQ
    •   .pcd 图片类型 MIMEimage/x-photo-cd 支持 5fafc84
    •   默认日志输出当前线程 Environment.CurrentManagedThreadId b8fe2cd
    •   app.UseInject(Action<UseInjectOptions>) 重载方法,简化配置 4.4.8 0b645fe
  • 突破性变化

    •   框架适配 .NET 6.0.9.NET 7.0 RC1 be5b40 1eee77b
    •   远程请求 .SetBodyBytes.SetFiles #I5PMS5 #I5PIYI
    •   FS.InitialContentTypeProvider() 名称为 FS.GetFileExtensionContentTypeProvider() 5fafc84
    •   远程请求 [BodyBytes] 设计,采用 HttpFile 方式 #I5PMS5 #I5PIYI
查看变化
public interface IHttp : IHttpDispatchProxy
{
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(HttpFile file);

// 支持多个文件
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(HttpFile[] files);

// 支持多个文件
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(IList<HttpFile> files);
}
// bytes 可以通过 File.ReadAllBytes(文件路径) 获取
var res = await "http://furion.baiqian.ltd/upload".SetContentType("multipart/form-data")
.SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync();

// 支持多个文件
var res = await "http://furion.baiqian.ltd/upload".SetContentType("multipart/form-data")
.SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync();
    •   所有的 AddInjectUseInject 参数设计 #I5QCF0
查看变化
public void ConfigureServices(IServiceCollection services)
{
services.AddInject(options =>
{
options.ConfigureSwaggerGen(gen =>
{
// ...
});
});
}
 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseInject(configure: options =>
{
options.ConfigureSwagger(swg =>
{
// ...
});

options.ConfigureSwaggerUI(ui =>
{
// ...
});
});
}
    •   远程请求所有 xxxAsStreamAsync 返回值 #I5QVEB
查看变化

由:

var stream = await "http://furion.baiqian.ltd/".GetAsStreamAsync();

改为:

var (stream, encoding) = await "http://furion.baiqian.ltd/".GetAsStreamAsync();
    •   .Inject() 支持配置更多参数,开放底层更多权限 4.4.9 1182283
查看变化
.Inject((builder, options) => {
options.ConfigureAppConfiguration((context, config) =>
{

});
options.ConfigureServices((context, services) =>
{

});
});
  • 问题修复

    •   远程请求代理模式非泛型参数导致数组溢出问题 #I5Q3SN
    •   LoggingMonitor 客户端 IP 记录错误 #I5QCU1 !562
    •   远程请求响应报文中包含 charset=gbk 进行序列化后乱码问题 #I5QVEB
    •   文件日志断电时丢失日志问题 db7d51b
    •   动态 WebAPI 或控制台贴了 [ApiDescriptionSettings(Tag = "")] 标签之后导致注释丢失 #I5REVF #I5RE4J
    •   启用数据库日志但是没有配置配置文件出现空异常问题 33817be
    •   控制台日志过滤无法过滤默认主机日志问题 33817be
    •   脚手架错误的日志配置问题 33817be
    •   高频压测情况下写日志并设置日志上下文导致并发更新出现 System.AggregateException 异常问题 #I5RFBQ
    •   日志文件名因 WindowsLinux 路径分隔符不一致导致日志文件创建失败问题,Linux 只支持 / 不支持 \ #I5RFBQ
    •   Oops.Oh/Bah 设置 .WithData 之后无效问题 !580
    •   基于 Redis 重写事件存储器序列化 IEventSource 实例异常问题 4.4.7 3e45020
    •   使用 Log 静态类超高频率下写日志导致 CPU 激增问题 4.4.7 #I5SDK5
    •   远程请求超高频率下发送请求导致 CPU 激增问题和异常问题 4.4.8 #I5SJJR
    •   集成第三方配置中心时获取的不是最新数据问题 4.4.9 2cdef6b
  • 其他更改

    •   JWTEncryption 静态类,支持无需注册 services.AddJwt() 使用 #I5PPKE #I5POLZ
    •   事件总线默认日志类名为 System.Logging.EventBusService #I5QLY5
  • 文档

    •   .NET6 升级 .NET7 文档
    •   ASP.NET 7 集成文档
    •   集成第三方配置中心文档 4.4.9
    •   第三方事件总线和 Furion 集成文档 4.4.9
    •   事件总线集成 Kafka 文档 #I5P5UG
    •   友好异常文档、日志记录文档、远程请求文档、依赖注入文档、即时通讯文档、事件总线文档、Worker Service 文档、单元测试文档、入门指南文档、数据库新增文档

v4.3.9(已发布)

版本细节
  • 新特性

    •   AppSettings 配置的 ExcludeAssemblies 属性,支持忽略指定程序集扫描 7b7747f
    •   Oops.OhOops.Bah 支持设置额外数据 .WithData(data) #I5O38E
    •   定时任务 Crontab.GetSleepMilliseconds(baseTime) 获取下一个发生时间的时间差 d024fae
    •   友好异常默认打印异常日志,避免生产环境漏掉重要异常信息 6e3a5bd
    •   日志静态类 Log.CreateLoggerFactory() 静态方法 75c672a
    •   多语言 SharedResource 模式,避免硬编程 18e80c7
    •   事件总线 MessageCenter 静态类,解决从 Fur v1.x 版本升级问题 a29fc7c
    •   组件化 IWebComponent 模式,支持 .NET5+ 08a44c3
    •   远程请求设置自己的 HttpClient 功能 #I5PBR3 !545
    •   LoggingMonitor 支持添加更多自定义配置 #I5PEPA
    •   LoggingMonitor 可配置 WithReturnValueReturnValueThreshold #I5PFJ1 #I5PFOW
    •   LoggingMonitor 可配置 MethodsSettings 更多信息 #I5PFJ1 #I5PFOW
查看变化
Serve.Run(RunOptions.Default
.AddWebComponent<XXXWebComponent>());

public class XXXWebComponent : IWebComponent
{
public void Load(WebApplicationBuilder builder, ComponentContext componentContext)
{
// ....
}
}
  • 突破性变化

    •   Furion 程序集 PublicKeyToken 强签名 26b12c0
    •   事件总线 IEventBusFactory 事件工厂方法 AddSubscriber -> SubscribeRemoveSubscriber -> Unsubscribe a29fc7c
    •   .AddInject().UseInject() 配置选项名称,移除 Configure 后缀 b6953cd
    •   远程请求 请求拦截响应拦截异常拦截 委托签名,新增 HttpClient 参数 #I5OWBO
查看变化
[Interceptor(InterceptorTypes.Request)]
static void OnRequest(HttpClient client, HttpRequestMessage req)
{
}

[Interceptor(InterceptorTypes.Response)]
static void OnResponsing(HttpClien client, HttpResponseMessage res)
{
}

[Interceptor(InterceptorTypes.Exception)]
static void OnException(HttpClient client, HttpResponseMessage res, string errors)
{
}
  • 问题修复

    •   生成包含 中文JWT Token 解密后出现乱码问题 #I5O397
    •   HttpRequestMessage 拓展中追加查询参数时的空引用异常 #I5PENW !547
    •   日志模块配置多个 IDatabaseLoggingWriter 只有一个生效 #I5PFQ2 #I5PFJ1
  • 其他更改

    •   默认输出文件日志模板,使其更加美观 #1518cf3
    •   默认规范化结果验证处理也支持状态码设置 2eb9390
    •   SqlSugarCore 拓展包和脚手架至 5.1.2.6 版本 #I5PCXK
    •   JSON Schema 关于 LoggingMonitor 更多配置 #I5PFJ1
  • 文档

    •   RabbitMQ 事件总线文档
    •   AppSettings 配置文档、事件总线文档、多数据库配置文档、日志文档、定时任务文档、MessageCenter 文档、远程请求文档、组件化文档、入门指南、多语言文档。

v4.2.13(已发布)

  • 新特性

    •   事件总线工厂,支持运行时动态添加订阅程序和移除订阅程序 #I5NNQX
    •   事件总线 [EventSubscribe] 事件 Id 支持正则表达式匹配 #I5NNQX
    •   事件总线 [EventSubscribe] 支持局部失败重试配置 #I5NNQX
    •   Log 全局静态类,方便随时随地记录日志 ba9b1f1
    •   事件总线 options.AddSubscriber(Type) 重载 42446078
    •   ValidationMetadata 类型 FirstErrorPropertyFirstErrorMessage 属性 #I5MFJT
    •   Serve.Run() 模式 WithArgs(args) 方法 #I5MOJB
    •   [UnitOfWork] 分布式事务 TransactionScope 支持 #I5MRTY
    •   16 位 MD5 加密支持 #I5N8RC
  • 突破性变化

    •   Scoped.Create(async (f,s) => {}) 异步创建作用域方法名称为 CreateAsync,避免一些情况下无法区分,同步方法不变 #I5N9XY
查看变化

由:

// Scoped.CreateUow 一样
await Scoped.Create(async (f, s) => {});

改为:

// Scoped.CreateUowAsync 一样
await Scoped.CreateAsync(async (f, s) => {});
    •   .NET 6.0.8.NET 7 Preview 7 842d4f7
    •   [LoggingMonitor] 命名空间为 System,因为使用频率越来越高 b879861
    •   在非 Web 环境中不正确使用字符串拓展方法检测机制 6389cbd
    •   所有 .Default 静态属性为 .Default() 方法 6389cbd
    •   工作单元 IUnitOfWork 所有方法参数类型,由 ActionExecutingContextActionExecutedContext 改为 FilterContext #I5MHX5
查看变化
public interface IUnitOfWork
{
void BeginTransaction(FilterContext context, UnitOfWorkAttribute unitOfWork);

void CommitTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork);

void RollbackTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork);

void OnCompleted(FilterContext context, FilterContext resultContext);
}
  • 问题修复

    •   日志上下文数据多次写入被清空问题以及数据库日志出现异常后停止写入 #I5LIWF
    •   个别情况下跨域默认配置的响应缓存导致嵌入式资源异常问题 7a57efe
    •   远程请求传入不合法的请求报文头数据触发校验失败问题 #I5LPFE
    •   多线程中使用静态日志写数据库日志导致连接池耗光问题 8d5cdd6
    •   EFCore 6.0 之后 IModelCacheKeyFactory 接口方法改变导致分表分库异常问题 #I5MCZ6 EFCore#25154 EFCore!3305
    •   ValidationMetadata 对象 Message 字符串类型出现 \"\" 问题 #I5MFJT
    •   [IfException] 覆盖 Oops.Oh/Bah 错误消息问题 4bbd854
    •   数据库日志写入循环写入和频繁创建数据库连接池问题 9ce214c
    •   Razor Pages 不支持全局异常拦截问题 #I5MHX5
    •   Razor Pages 不支持全局数据验证问题 #I5MHX5
    •   Razor Pages 不支持工作单元 [UnitOfWork] 问题 #I5MHX5
    •   Razor Pages 不支持 EFCore 自动 SaveChanges 问题 #I5MHX5
    •   Blazor Serverv4.2.2 版本更新导致的问题 #I5MNFN
    •   [IfException] 不支持多语言配置问题 #I5MPN7
    •   通过 services.AddMvcFilter<LoggingMonitorAttribute>() 方式注册无效问题 8d1477d
    •   事件总线默认 Channel 管道初始化时机过晚问题,解决部分第三方依赖使用问题 #I5MM3O
    •   主机停止时写入日志异常问题 #I5N7S2
    •   数据库上下文手动释放导致 AutoSaveChange 特性出现释放异常问题 #I5NFWC
    •   [LoggingMonitor] 循环引用序列化问题 #I5NRT9
    •   远程请求传入 null Body 参数抛出空异常问题 #I5NTUE
    •   事件总线默认开启模糊匹配(正则表达式)导致不必要的订阅 #I5NVOP
  • 其他更改

    •   事件总线默认 Channel 管道初始化时机,解决部分第三方依赖使用问题 #I5MM3O
    •   底层迭代改进优化
    •   规范化文档获取控制器、方法分组、标签信息 66d8d54
  • 文档

    •   全局日志静态类 Log 文档 ba9b1f1
    •   NuGet 本地测试包文档
    •   日志文档、静态类文档、数据校验文档、Worker Service 文档、工作单元文档、依赖注入文档

v4.1.14(已发布,全新单元测试)

关于单元测试

单元测试和集成测试是保证一个系统能够持续维护和稳定运行的必备技能,但是目前现有的单元测试组件无法直接集成 Furion 的功能,最常用的就是如何在单元测试中读取配置,以及如何进行依赖注入

在过去,Furion 只能不断的去调整,以至于适配第三方单元测试写法,搞得不伦不类!

所以,这一次不再妥协,Furion 推出自己的单元测试工具,可以让现有的单元测试如 Xunit 100% 支持 Furion 所有功能,全部保证一致的写法。

  • 新特性

    •   Furion.Xunit 拓展包,正式实现 Xunit 单元测试完整支持 Furion 063a034e
    •   services.AddMonitorLogging() 日志监视器服务,支持非常灵活的日志操作 81df742
    •   Serve.Run(silence: true) 等一系列强大的静默启动功能 #I5JBSQ #I5J98T 7cced4
    •   SpecificationDocumentBuilder.GetOpenApiGroups() 方法获取底层的规范化接口分组信息 4ff03c5
    •   logger.ScopeContext() 配置日志上下文功能 #I5JC0D
    •   跨域配置 CorsAccessorSettings.SignalRSupport 配置选项,支持配置 SignalR 跨域 #I5JREM
    •   事件总线 UseUtcTimestamp 选项配置,可选择使用 DateTime.UtcNow 还是 DateTime.Now,默认是 DateTime.Now #I5JSEU
    •   规范化文档 [OperationId] 配置,解决自定义 Swagger UI 不能正确显示路由问题 #I5K1IB
    •   远程请求 IHttpDispatchProxy 方式全局拦截支持多态(继承) #I5K8FS
  • 突破性变化

    •   Furion.Xunit 拓展包,正式实现 Xunit 单元测试完整支持 Furion 063a034e
    •   Furion.Extras.DatabaseAccessor.SqlSugar 拓展插件中的 [SqlSugarUnitOfWork] 工作单元特性,将使用通用工作单元替换 查看最新实现文档
    •   Inject.Create() 方法,再也不需要了,框架提供了无敌强大的 Serve.Run() 静默启动方式 200848e
    •   Serve.RunConfigureConfiguration 方法参数,由 configuration => {} 改为 (environment, configuration) => {} 83c97bb
查看变化
// 由
Serve.Run(RunOptions.Default.ConfigureConfiguration(configuration => {

}));
// 改为:
Serve.Run(RunOptions.Default.ConfigureConfiguration((environment, configuration) => {

}));
  • 问题修复

    •   [LoggingMonitor] 异常消息日志级别为 Information 错误问题 ab46cdf
    •   新版本日志组件频繁提示文件占用问题,将文件独占锁改为共享锁 #I5J3S6
    •   配置数据库日志读写器为 EFCore 时控制台出现无限打印问题 #I5J474
    •   [LoggingMonitor] 针对 byte[] 类型参数输出过大问题 5380f35
    •   友好异常和规范化结果丢失了原始 ErrorCode 问题 #I5IX2R
    •   新版本日志组件自定义数据库读写器注入 IRepository 仓储导致死循环问题 #I5IX2R
    •   Mvc 默认手动验证和 Furion 全局验证冲突问题 2a06c39
    •   Serve.Run() 模式不支持 SuperSocket 第三方包问题,原生是支持的。186ca0a
    •   SignalR 跨域错误问题 #I5JREM
    •   [LoggingMonitor]Oops.OhOops.Bah 记录到了错误日志中,默认应该是 Information 且提供可配置 #I5JZ1H
    •   自定义 Swagger UI 之后个别 UI 要求必须配置 operationId,否则出现 guid 序号 #I5K1IB
    •   主动抛出 NotFoundResultNotFoundObjectResult 无效问题 #I5KALZ
    •   [LoggingMonitor] 解析方法参数但前端未传入时出现错误问题 #I5KC5P
    •   [LoggingMonitor] 无法序列化 IQueryable 返回值问题 #I5KJD1
    •   [LoggingMonitor] 不能记录全局验证错误问题 b44087d
    •   [LoggingMonitor] 存在注册顺序差异问题 b44087d
  • 其他更改

    •   底层的规范化文档 SpecificationDocumentBuilder 部分方法,提供更加便捷的第三方 Swagger UI 集成 10f0f01
  • 文档

    •   单元测试文档、入门指南文档、Worker Services 文档
  • 本期亮点

  1. Serve.Run() 彻彻底底支持全平台,提供非常强大的静默模式

启用静默模式可以实现无阻塞方式执行程序,而且还能体验完整的 Furion 功能。

查看变化
大革命

有了 Serve.Run() 静默模式后,Furion 彻彻底底支持全平台,不管你是 控制台、Web、桌面、移动、单元测试,集成测试,基准测试等等应用程序

Serve.Run(silence: true);

// 不会阻塞执行哦,而且从这里开始可以使用 Furion 任何功能,比如 App.Configuration....
Console.WriteLine("Hello, World!");
Console.ReadKey();

还有更多静默模式。

// RunOptions 方式
Serve.Run(RunOptions.DefaultSilence);

// LegacyRunOptions 方式
Serve.Run(LegacyRunOptions.DefaultSilence);

// GenericRunOptions 方式
Serve.Run(GenericRunOptions.DefaultSilence);
  1. 强大的 Furion.Xunit 单元测试、集成测试
查看变化

单元测试中初始化 Furion

using Furion.Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

// 配置启动类类型,第一个参数是 TestProgram 类完整限定名,第二个参数是当前项目程序集名称
[assembly: TestFramework("TestProject1.TestProgram", "TestProject1")]

namespace TestProject1;

/// <summary>
/// 单元测试启动类
/// </summary>
public class TestProgram : TestStartup
{
public TestProgram(IMessageSink messageSink) : base(messageSink)
{
// 初始化 Furion
Serve.Run(silence: true);
}
}

测试类支持完整依赖注入

using TestProject1.Services;
using Xunit;

namespace TestProject1;

public class UnitTest1
{
private readonly ICalcService _calcService;
public UnitTest1(ICalcService calcService)
{
_calcService = calcService;
}

[Fact]
public void 测试两个数的和()
{
Assert.Equal(3, _calcService.Plus(1, 2));
}
}

查看新版本单元测试文档

  1. 开放底层规范化文档分组接口,使得集成第三方 Swagger UI 更加容易,如集成 IGeekFan.AspNetCore.Knife4jUI 拓展:
查看变化
安装包

只需要在 YourPoject.Web.Core 层安装 IGeekFan.AspNetCore.Knife4jUI 即可。

3.1.1 Knife4jUI 独立版本配置

var routePrefix = "api";    // 定义 swagger 路由地址,如果是跟目录,设置 string.Empty 即可

app.UseKnife4UI(options =>
{
options.RoutePrefix = routePrefix; // 配置 Knife4UI 路由地址
foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups())
{
options.SwaggerEndpoint("/" + groupInfo.RouteTemplate, groupInfo.Title);
}
});

app.UseInject(routePrefix); // 配置 Furion 路由地址

3.1.2 Knife4jUISwagger 共存版本配置

app.UseKnife4UI(options =>
{
options.RoutePrefix = "newapi"; // 配置 Knife4UI 路由地址,现在是 /newapi
foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups())
{
options.SwaggerEndpoint("/" + groupInfo.RouteTemplate, groupInfo.Title);
}
});

app.UseInject(); // Furion 默认 api 地址为 /api

如需实现登录之后自动将 token 添加到头部可在登录接口 AfterScript 执行以下代码:

ke.global.setAllHeader(
"Authorization",
"Bearer " + ke.response.headers["access-token"]
);
  1. 提供强大的日志上下文功能
// 写法一
_logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10))
.LogInformation("我是一个日志 {id}", 20);

// 写法二
_logger.ScopeContext(new Dictionary<object, object> {
{ "Name", "Furion" },
{ "UserId", 10 }
}).LogInformation("我是一个日志 {id}", 20);

// 写法三
_logger.ScopeContext(new LogContext {
// ....
}).LogInformation("我是一个日志 {id}", 20)

v4.0.0(重新起航)💖

不忘初心,感恩遇见,感恩信任

2020 年 09 月 01 日,一个叫 Fur 的开源项目在 Gitee 的襁褓中悄然诞生,她的出生仿佛带着某种使命,没有包袱,无限可能。

她缓缓的张开双眼,干净雪亮的眼睛似乎对这个世界充满了好奇,任何事物在她眼前晃过都像是直击灵魂的思想碰撞,这些在她看来都是非常宝贵的财富。她貌似有用不完的精力,一路汲取知识,升级打怪,不断奔跑,乐此不疲。

记得 2020 年 11 月 11 日的单身节,她迎来了“一岁(v1.0.0)”生日,自那以后,IT 这个大银幕上频繁出现她的身影,越来越多 .NET5 开发者转粉,像是告诉这个世界,她就是 IT 界大明星。

每一个明星都有一个好听的艺名,她当然也不例外,2020 年 11 月 20 日,经纪人百小僧为她起名为 Furion

2021 年 11 月 09 日起,她进入了每个孩子都经历过的叛逆期,年少轻狂喜新厌旧,抛弃了曾经支持她的 .NET5 粉丝们,投入到新的 .NET6 拥趸者怀抱中,自此过上了奢靡富足的生活。

但她过的不开心,时常在夜里想起 .NET5 的粉丝们,内心非常自责,但在双重工作压力下她毅然选择了忽视他们的诉求,仿佛他们就是累赘。

时间真的是好东西,曾经认为是对的,经过岁月的蹉跎历磨,渐渐的明白:不忘初心,方能始终。

这一次,不落下一人(.NET5.NET6,...,.NET N),携手共进,重新起航,感恩遇见,感恩信任。

  • 新特性

    • v4.0.0 支持 .NET5.NET6,...,.NET N,所有的 Furion 项目都能够升级到该版本,重新起航,实现大统。

v3.9.2(已发布,全新日志组件)

关于日志

日志模块是任何应用系统都必备的功能,可以说是最重要的模块!在 .NET 社区中有 Log4NETNLogSerilog 等日志组件,它们无一不是优秀的开源项目。

但由于这些日志组件历史悠久,内部兼容的 .NET 版本非常多,功能随着时间推移变得极其强大复杂,在实际项目使用中,发现每一个日志组件配置总是不那么友好,特别是在使用上不够简单

Furion 作为全栈开发框架,在过去版本并没有提供足以满足开发者需求的日志模块,转而推荐大家集成第三方组件,如 Serilog导致后续无法实现自定义功能和也增加了不少维护成本。

这一次,Furion 不再妥协,彻底重构了日志模块,实现日志功能/需求完全自主可控,提供给开发者几乎所有日志功能的需求!

Furion 的使用者们,是时候“更换”掉第三方日志组件,让我们一起迭代出更强更好的日志组件吧! 🍖

  • 新特性

    •   日志模块,内置写入控制台、文件、数据库功能,再也无需引入第三方日志了!日志源码
    •   强大的调试日志 [LoggingMonitor] 32dfc1
  • 突破性变化

    •   日志模块,内置写入控制台、文件、数据库功能,再也无需引入第三方日志了!日志源码
    •   TP.Wrapper 规范化日志模板算法,由过去的 [属性] 改为 ##属性##,解决 JSON 内容冲突问题 394ecec
    •   未来 Furion.Extras.Logging.Serilog 拓展将不再继续维护,因为 Furion 有强大的日志组件了!
  • 问题修复

    •   脱敏模块模型绑定个别情况下空异常问题 #I5IM5C
  • 文档

    •   日志文档、静态类文档、数据校验文档
  • 本期亮点

  1. 极易使用且强大的日志模块
查看变化
// 写入文件
services.AddFileLogging("logs/application.log");

// 写入数据库
services.AddDatabaseLogging<DatabaseLoggingWriter>();

查看更多日志文档

  1. 强大的 [LoggingMonitor] 调试日志
查看变化
using Furion.Logging;

namespace Furion.Application;

public class TestLoggerServices : IDynamicApiController
{
[LoggingMonitor]
public PersonDto GetPerson(int id)
{
return new PersonDto
{
Id = id
};
}
}

支持控制器、操作或全局注册拦截

输出日志为:

┏━━━━━━━━━━━  Logging Monitor ━━━━━━━━━━━
┣ Furion.Application.TestLoggerServices.GetPerson (Furion.Application)

┣ 控制器名称: TestLoggerServices
┣ 操作名称: GetPerson
┣ 路由信息: [area]: ; [controller]: test-logger; [action]: person
┣ 请求地址: https://localhost:44316/api/test-logger/person/11
┣ 来源地址: https://localhost:44316/api/index.html
┣ 浏览器标识: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62
┣ 客户端 IP 地址: 0.0.0.1
┣ 服务端 IP 地址: 0.0.0.1
┣ 服务端运行环境: Development
┣ 执行耗时: 31ms
┣ ━━━━━━━━━━━━━━━ 授权信息 ━━━━━━━━━━━━━━━
┣ JWT Token: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIkFjY291bnQiOiJhZG1pbiIsImlhdCI6MTY1ODcxNjc5NywibmJmIjoxNjU4NzE2Nzk3LCJleHAiOjE2NTg3MTc5OTcsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.VYZkwwqCwlUy3aJjuL-og62I0rkxNQ96kSjEm3VgXtg

┣ UserId (integer): 1
┣ Account (string): admin
┣ iat (integer): 1658716797
┣ nbf (integer): 1658716797
┣ exp (integer): 1658717997
┣ iss (string): dotnetchina
┣ aud (string): powerby Furion
┣ ━━━━━━━━━━━━━━━ 参数列表 ━━━━━━━━━━━━━━━
┣ Content-Type:

┣ id (Int32): 11
┣ ━━━━━━━━━━━━━━━ 返回信息 ━━━━━━━━━━━━━━━
┣ 类型: Furion.Application.Persons.PersonDto
┣ 返回值: {"Id":11,"Name":null,"Age":0,"Address":null,"PhoneNumber":null,"QQ":null,"CreatedTime":"0001-01-01T00:00:00+00:00","Childrens":null,"Posts":null}
┗━━━━━━━━━━━ Logging Monitor ━━━━━━━━━━━

v3.8.9(已发布)

  • 新特性

    •   规范化结果 ExceptionMetadataValidationMetadata 都可以获取 ErrorCode 属性 #I5GJ6D
    •   ValidationMetadataStatusCode 属性 #I5HB5L
    •   远程请求对 Url 是否编码设置,[Get(WithEncodeUrl = false)]WithEncodeUrl(false) #I5GOBC
    •   更强大的 JWTEncryption.SecurityReadJwtToken('token') 读取解析 Token 静态方法 574eeb6
    •   ValiationTypes.Html 验证 Html 标签 #I5HBKC
    •   EFCore.NamingConventions 支持,可自定义生成表名,字段名风格,比如小驼峰,蛇形命名等 #I5HBEI
    •   INamedServiceProvider 命名服务提供器,可解析接口多实现 #I5HF98
    •   脱敏处理模块方法参数单个值处理 a22ec3c
    •   脱敏词库支持 | 分割词语 3106b1d
  • 突破性变化

    •   所有 .NET 依赖包至 6.0.7 版本
    •   Serilog.AspNetCore 包至 6.0.0 版本
    •   JWT Token 刷新逻辑 #I5GXML 574eeb6
    •   ExceptionMetadata 命名空间为 Furion.FriendlyException 3105d16
    •   Retry.Invoke(Func<Task>...) 为:Retry.InvokeAsync(Func<Task>...) 3b78999
    •   脱敏处理模块,大大提高性能和准确率,同时支持方法参数单个值处理 a22ec3c
  • 问题修复

    •   Rider 开发工具对同名脚手架 (EFCoreSqlSugar) 只显示一个问题 !518
    •   UnitOfWork 工作单元在 EFCore 中失效问题 #I5H0T3
    •   JWTToken 如果存在数组类型的值时,刷新 Token 后丢失了历史值 #I5GXML
    •   远程请求 WithEncodeUrl 无法在 [HttpMethod] 设置问题 574eeb6
    •   Serve.Run() 模式下添加自定义配置导致 EFCore 无法获取自定义配置文件问题 #I5GZ0F
    •   Oops.Bah 进入全局异常拦截器问题 #I5H47S
    •   AddDbPool/AddDb 扩展未根据配置 Key 路径读取问题 #I5H6S4 !520
    •   ValiationTypes.Url 正则表达式覆盖不全问题 #I5HBKC
    •   v3.5.x 版本导致集成 EFCore.NamingConventions 失效问题 #I5HBEI
    •   Swagger 长路由不支持问题以及 [Required] 配置 AllowEmptyStrings 无效问题 c014330
    •   远程请求上传文件时请求报文 boundaryContent-Disposition 设置不正确问题 #I5HEF0
    •   脱敏模块替换敏感词汇出现多替换问题 a22ec3c
  • 其他更改

    •   多语言默认处理逻辑,允许不配置任何语言,过去版本会报错 #I5GRD9 5077c5d
    •   规范化文档 Swagger 性能
    •   MongoDB 仓储 TDocument 泛型约束 3f49055
  • 文档

    •   远程请求文档,日志记录文档、多数据库文档、PM2 部署文档、Visual Studio 高效率文档

v3.7.11(已发布)

  • 新特性

    •   Minimal API 应用支持:.AddInjectMini() #I4KOQ5
    •   跨域 WithExposedHeaders 默认配置 access-tokenx-access-token 42ebdfd
    •   脚手架默认启用 app.UseHttpLogging() HTTP 日志 42ebdfd
    •   FurionASP.NET Core 完整 json 配置的 JSON Schema 架构 JSON Schema
    •   Sql 代理支持返回单个类类型参数 1d7fb5b
    •   Sql 代理支持返回 ValueTuple 单个类类型参数 876a2f5
    •   组件化设计模块,支持比 AppStartup 更灵活便捷的设计 #components
    •   独立工作单元模块,支持任何第三方 ORM a02413d
    •   跨域 FixedClientToken 配置参数 bd01638
    •   throw Oops.Bah 可以手动触发规范化验证失败处理 83f0036
    •   FriendlyExceptionSettingsThrowBah 配置,可标记 Oops.Oh 不进入异常处理 76ffa7f
  • 突破性变化

    •   Minimal API 应用支持:.AddInjectMini() #I4KOQ5
    •   FurionASP.NET Core 完整 json 配置的 JSON Schema 架构 JSON Schema
    •   组件化设计模块,支持比 AppStartup 更灵活便捷的设计 #components
    •   独立工作单元单元模块,支持任何第三方 ORM a02413d
    •   DataValidationFilterFriendlyExceptionFilter,解决不支持手动抛出业务异常问题 83f0036
    •   .AddDb<>.AddDbPool<> 自定义委托参数签名,由 Action<DbContextOptionsBuilder> 改为:Action<IServiceProvider, DbContextOptionsBuilder>
查看变化
// 由:
options.AddDbPool<TDbContext>(DbProvider.MySql, opt => {
});
// 改为
options.AddDbPool<TDbContext>(DbProvider.MySql, (services, opt)=> {
})
  • 问题修复

    •  v3.6.3 版本依赖,执行原生 Sql 添加了参数校验导致存储过程执行错误问题 #I5ERMQ
    •   tools/cli.ps1 脚本工具出现数据库链接被占用问题
    •   JWTSettings 算法配置 JSON Schema 错误问题,感谢 @gitwentao #I5G27B !516
    •   基于策略授权在不配置 Policy 的情况下出现空异常问题 #I5EVF2
    •   启用数据库实体跟踪时导致新增实体多次查询数据库问题 #I4J2LZ
    •   不启用规范化结果导致验证失效,异常失效问题 cdb3f57
    •   验证异常和友好异常冲突问题 83f0036
    •   CentOS 7.9 系统部署无法指定命令 --urls 参数问题 8cc8ee
  • 其他更改

    •   脚手架所有 .json 文件,默认添加 JSON Schema 支持
  • 文档

    •   组件化启动文档
    •   Vue/React/Angular 请求代理文档
    •   JSON Schema 文档,支持配置智能提示和验证
    •   跨域文档、规范化文档、配置文档、日志文档、IIS 部署文档
  • 本期亮点

  1. 新增 JSON Schema 支持,所有 .json 文件支持智能提示和验证
查看变化
{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",

"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"AllowedHosts": "*"
}

查看更多 JSON Schema 文档

  1. 根据 Swagger 生成 Vue/React/Angular 前端请求代码

查看文档

  1. Sql 代理支持返回单个类类型参数
查看变化
public interface ISql : ISqlDispatchProxy
{
// 集合类型
[SqlExecute("select * from person")]
List<Person> GetPersons();

// 自 v3.7.3+ 版本支持返回单个类类型参数
[SqlExecute("select * from person where id=@id")]
Person GetPerson(int id);
}
  1. Sql 代理支持返回 ValueTuple 单个类类型参数
查看变化
public interface ISql : ISqlDispatchProxy
{
[SqlExecute(@"
select * from person where id =@id;
select * from person")]
(Person, List<Person>) GetData(int id); // 注意返回值是 `(Person, List<Person>)` 组合
}
  1. 支持 Minimal API 应用
查看变化

了解 Minimal API 应用

var builder = WebApplication.CreateBuilder(args).Inject();

// 注册 Minimal 服务
builder.Services.AddInjectMini();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseInject(string.Empty);

app.MapGet("/hello", () =>
{
return "Hello, Furion";
});

app.Run();

v3.6.9(已发布,全新入口组件)

关于入口组件

相信从 ASP.NET 5 升级至 ASP.NET 6 的开发者都经历过微软多次变更初始化主机 Program.csStartup.cs 写法,甚至在 .NET6+ 之后移除了 Startup.cs 的设计。

试问,ASP.NET 7ASP.NET 8 ... ASP.NET N 呢?会不会每一个版本都有不同的初始化方式,那后续项目如何无缝升级?

所以,为了保证一致的代码体验和后续无缝升级,推出了 Serve.Run(),即使未来创建方式变了,也不用担心,交给框架即可。

Serve.Run 模式也标记着 Furion 进入全新的极简入门时代。

  • 新特性

    •   Serve.Run() 极简主机模式,真正实现极速入门。95cac5b
    •   TP.Wrapper(...) 拓展方法,主要用来生成规范化的日志模板 427999a
    •   项目类型为 <Project Sdk="Microsoft.NET.Sdk"> 的控制台项目 fb08a65
    •   BadPageResult 错误页面类型 !494
    •   [SchemaId] 特性,解决不同程序集相同的类名生成 SwaggerSchemaId 冲突问题 #I5D3CU
    •   远程请求 options.ApproveAllCerts() 忽略所有客户端证书拓展 eb7d18a
    •   判断是否是单文件环境部署静态属性 App.SingleFileEnvironment de556f0
    •   WebApplicationBuilder.UseSerilogDefault() 拓展 e02524c
  • 突破性变化

    •   项目类型为 <Project Sdk="Microsoft.NET.Sdk"> 的控制台项目 fb08a65
    •   Serve.Run() 极简主机模式,真正实现极速入门。95cac5b
    •   未启用规范化结果时,MVC 验证失败返回 BadPageResult() 页面类型 !494
  • 问题修复

    •   默认注册的 services.AddResponseCaching(); 服务导致 .axd 内嵌资源请求错误问题 !495
    •   Oracle 数据库执行 sql 必须要求命令参数和 sql 语言参数数量一致 #I5D057
    •   IHostService 类型不能自动注册问题,之前只扫描了 BackgroundService 派生类 968344
    •   国产芯片主机不能识别 dotnet run --urls 参数问题 6d4398
    •   远程请求上传文件不支持特定文件后缀问题,如 .pem 文件 ba42198
    •   一些程序集已破坏或程序集不完整导致主机无法启动问题 d2dc3e4
    •   远程请求传入 headers 时类型为 Dictionary<string, string> 导致转换异常问题 #I5DHL9
    •   Serilog 单文件发布不生成日志文件 I5DQ2B
  • 其他更改

    •   远程请求默认客户端不检查 SSL 证书 eb7d18a
    •   开放验证服务选项 SuppressModelStateInvalidFilter 属性为可配置 !494
  • 文档

    •   Serve.Run() 文档
    •   HttpContext 文档
    •   GlobalUsings 文档
    •   TP 全局静态类文档
    •   中间件文档、筛选器文档、审计日志文档
    •   跨域文档、远程请求文档
  • 精彩贡献

    • !494 优秀 Pull Request 辩论典范
  • 本期亮点

  1. 极速入门
查看变化
Program.cs
Serve.Run();

[DynamicApiController]
public class HelloService
{
public string Say()
{
return "Hello, Furion";
}
}

启动浏览器查看效果,惊呆了吗!

  1. 内置错误页
查看变化
using Furion.FriendlyException;

public IActionResult Add(Person person)
{
if(!ModelState.IsValid)
{
return new BadPageResult();
}
}
  1. Swagger 支持 Markdown
查看变化
/// <summary>
/// 测试 Markdown
/// </summary>
/// <remarks>
/// # 测试 `Markdown` 注释
///
/// ![](https://localhost:44316/images/logo.png)
///
/// ```cs
/// Serve.Run();
///
/// [DynamicApiController
/// public class HelloService
/// {
/// public string Say()
/// {
/// return nameof(Furion);
/// }
/// }
/// ```
///
/// 功能还不错!!!
///
/// | 商品 | 价格 | # 其他 |
/// |--------------|-----------|------------|
/// | Juicy Apples | 1.99 | *7* |
/// | Bananas | **1.89** | 5234 |
/// | Bananas | **1.89** | 5234 |
/// | Bananas | **1.89** | 5234 |
///
/// -----
///
/// # Furion 探索版
///
/// > 在过去一年,实现 `Furion` 从无到有,编写文档已逾百万字,过程心酸开源人自知。
/// >
/// > 这一路日夜兼程,嘲讽批评常伴眼耳,即便辛苦无奈、想过放弃,但为了那微不足道的存在感依然努力着。
/// >
/// > 当然,也收获了不少...越来越多拥趸者,越发精湛技术能力,更高层次思维模式,还有许多跨界跨行朋友。
/// >
/// > 在 《[开源指北] (https://gitee.com/opensource-guide/comments/)》中,我曾说道:“开源如同人的脸,好坏一面便知,缺点可能会受到嘲讽批评,优点也会收获赞扬尊重。别担心,他们正在塑造更好的你。”
/// >
/// > 所以,这一次重新起航,重塑 `Furion` 重塑自己。也许未来在某个 IT 圈但凡有人谈起 `.NET` 还能瞟到 `Furion` 的身影。
///
/// ---
///
/// 🎉 探索 Furion 未来更多可能性,实现无第三方依赖的版本,所有模块功能按需安装,按需加载。
///
/// - 作者:[百小僧] (https://gitee.com/monksoul)
/// - 日期:2021 年 08 月 30 日
///
/// ## 环境
///
/// - IDE :[Microsoft Visual Studio Enterprise 2022 Preview(64 位) 版本 17.0.0 Preview 3.1] (https://visualstudio.microsoft.com/zh-hans/vs/preview/)
/// - SDK :[.NET SDK 6] (https://dotnet.microsoft.com/download/dotnet/6.0)
/// - 语言:[C# 10](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10)
///
/// ## 包说明
///
/// - `Furion.Core`:无第三方依赖,可在 `.NET 6` 所有项目类型中运行。
/// - `Furion`:内部依赖 `Furion.Core` 且无第三方依赖,**聚焦 `Web` 应用**,采用共享框架 `<FrameworkReference Include="Microsoft.AspNetCore.App" />` 模式
/// </remarks>
public void MarkdownTest()
{
// ....
}

v3.5.7(已发布)

  • 新特性

    •   Options 选项属性支持自定义 Key 名称,[MapSettings("key")] #I5B2HN
    •   事件总线模块事件 Id 支持枚举类型 2f328aa
    •   事件总线模块发布者 PublishAsyncPublishDelayAsync 重载 2f328aa
    •   事件总线模块拓展方法:Enum.ParseToString()String.ParseToEnum() 2f328aa
    •   FurionSqlSugar 脚手架 🆕🆕🆕 8d9293d
    •   Dapper 拓展全局配置委托 #I5AYFX
    •   sql 转实体支持多种命名策略(纯大写,纯小写,带下划线分割等等),如 Oracle 数据库 a90e245
    •   FS.InitalContentTypeProvider() 拓展方法,获取系统内所有支持的 Content-Type 文件提供器 6099900
  • 突破性变化

    •   彻底解决了 Furion 不能单文件发布的问题 7e8e0b7
  • 问题修复

    •   框架规范化文档 Swagger 不支持 Controller 派生类 api 路由问题,原生 ASP.NET 是支持的 29e47bc
    •   基于 Schema 多租户配置无效问题 6f820ce
    •   指定实体 [Table(schema:"dbo")] 特性后 Schema 无效问题 6f820ce
    •   数据库视图不支持 Schema 配置问题 6f820ce
    •   规范化结果极端情况下出现 空异常 问题 c9b0ef
  • 其他更改

    •   axios-utils.tsangular-utils.ts ,新增请求拦截携带刷新 Token 的时机判断 82f89bd
    •   规范化文档 Swagger 加载继承注释 <inheritoc /> 性能小优化 5f06880
    •   脚手架模板,新增 GlobalUsings.cs 模式
    •   对象映射默认支持忽略大小写 !486
  • 文档

    •   Furion 单文件发布文档
    •   Furion + SqlSugar 脚手架文档
    •   事件总线文档、选项文档、即时通讯文档、.NET5 升级 .NET6 文档、依赖注入文档、跨域文档、数据加解密文档
  • 本期亮点

  1. 事件总线 Id 支持枚举类型
查看变化
EventSubscribe("TO:DO")]  // 字符串类型
public async Task EventHandler1(EventHandlerExecutingContext context)
{
// ....
}

[EventSubscribe(YourEnum.Some)] // 枚举类型
public async Task EventHandler2(EventHandlerExecutingContext context)
{
var eventEnum = context.Source.EventId.ParseToEnum(); // 将事件 Id 转换成枚举对象
// ....
}
  1. 事件总线发布支持更简单调用
查看变化
// 旧版本
await _eventPublisher.PublishAsync(new ChannelEventSource("ToDo:Create", name));

// 新版本
await _eventPublisher.PublishAsync("ToDo:Create", name);
await _eventPublisher.PublishAsync(YourEnum.Some); // 也支持枚举
  1. 选项支持属性自定义配置 Key
查看变化
"AppInfo": {
"Name": "Furion",
"Version": "1.0.0",
"Company_Name": "Baiqian" // 可以和属性不一样
}
public class AppInfoOptions : IConfigurableOptions
{
public string Name { get; set; }
public string Version { get; set; }

[MapSettings("Company_Name")] // 支持自定义
public string Company { get; set; }
}
  1. 日志规范化模板
查看变化
// 生成模板字符串
var template = TP.Wrapper("Furion 框架", "让 .NET 开发更简单,更通用,更流行。",
"[作者] 百小僧",
"[当前版本] v3.5.3",
"[文档地址] http://furion.baiqian.ltd",
"[Copyright] 百小僧, 百签科技(广东)有限公司");
Console.WriteLine(template);

输出结果

┏━━━━━━━━━━━  Furion 框架 ━━━━━━━━━━━
┣ 让 .NET 开发更简单,更通用,更流行。

┣ 作者: 百小僧
┣ 当前版本: v3.5.3
┣ 文档地址: http://furion.baiqian.ltd
┣ Copyright: 百小僧, 百签科技(广东)有限公司
┗━━━━━━━━━━━ Furion 框架 ━━━━━━━━━━━


v3.4.2(已发布)

  • 新特性

    •   规范化文件 EnableAllGroups 功能,可以将多个分组合并到一个分组中 9277b98
    •   angular-utils 客户端工具库,专门处理 angular 项目接口代理问题 6c70584
    •   Swagger 支持单个接口更多描述功能(支持 htmle5e1db0
    •   Swagger 接口 [Obsolete] 过时支持功能 e5e1db0
    •   动态 API[ApiDescriptionSettings] 特性 和DynamicApiControllerSettings 配置 的 ForceWithRoutePrefix 参数,支持强制复写 [Route] 特性并添加 DefaultRoutePrefix #I59B74
  • 突破性变化

    •   默认内置 GBKWindows-1252, Shift-JIS, GB2312 等编码支持 c456ecb
    •   FurionSqlSugar 脚手架
  • 问题修复

    •   <inheritdoc /> 不能跨程序集问题 3b9d39c
    •   <inheritdoc /> 不支持带参数,不支持隐式实现接口注释问题 #I59A6W#note_10699021
    •   v3.3.1 版本导致 Swagger 不能显示问题 6763352
    •   远程请求、JSON以及 Web 页面不支持 GBKGB2312 等国标编码问题 c456ecb
    •   远程请求响应报文设置了 Content-Type:charset= 不能自动转换编码问题 c456ecb
  • 其他更改

    •   axios-utils.tsangular-utils.ts 多客户端支持
  • 文档

    •   GlobalUsings 文档 文档地址
    •   请求大小/上传大小限制文档 文档地址
    •   规范化文档,Worker Service 文档,动态 API 文档
  • 本期亮点

  1. 启用 All Groups 分组功能
查看变化

有时候我们为了更好的对接口进行归类,配置了 Swagger 多个分组的功能,但这样也对生成客户端请求代码造成了困扰,所以添加了新的配置:

{
"SpecificationDocumentSettings": {
"EnableAllGroups": true
}
}
  1. 接口过时控制
查看变化

当我们某个接口已经过时,提示尽早调用最新接口,只需要在方法上面贴 [Obsolete] 即可,如:

[Obsolete("GetName() 已经过时,请调用 GetFrameworkName() 替代")]
public string GetName()
{
return nameof(Furion);
}

[Obsolete]
public string Other()
{
// ...
}
  1. 单一接口更多描述
查看变化

在该版本新增了 [ApiDescriptionSettings]Description 属性,支持定义更多描述,如:

[ApiDescriptionSettings(Description = "我是一段描述,显示更多内容 <button>我是按钮</button>")]
public string add()
{
//....
}

v3.3.3(已发布)

  • 新特性

    •   远程请求文件上传自动识别 Content-TypeMime #I57ZMN
    •   远程请求方法支持设置 Content-TypeEncoding #I57ZMN
    •   根据文件名获取 Content-TypeMime 类型 #8f78184
    •   规范化文档支持授权访问 #32aa3b6
    •   代码注释,规范化文档注释 inheritdoc 语法支持 ❤️️️️ #159A6W
    •   Vue2/3React 16.8+Angular 9+ 前端请求工具库,实现后端 API 代理 axios-utils
  • 突破性变化

    •   代码注释,规范化文档注释 inheritdoc 语法支持 ❤️️️️ #159A6W
    •   .NET 所有依赖包至 v6.0.5 版本
  • 问题修复

    •   自定义全局异常 Exception 后导致获取错误行号,文件空异常问题 #I53EGM
    •   配置数据库上下文传递空委托导致空引用异常问题 #I519AW
    •   字符串模板模板 Render 拓展方法返回 void 问题,应该返回 string Github-#99
    •   远程请求文件上传出现空情况问题(原因是缺失 Content-TypeI57ZMN
  • 其他更改

    •   框架源码引入 GlobalUsings 机制,减少代码体积 #7e9cc1c
    •   跨域请求的预检设置,如果未设置,则默认为 24 小时,主要解决前端多次发送 204 预检问题 4a11e7c
    •   视图引擎反射性能
  • 文档

    •   粘土对象序列化 JSON 配置文档
    •   前端解密 JWT 文档
    •  byte[]url 文档
    •   二级虚拟目录部署文档,远程请求文档,文件上传文档,安全授权文档、规范化文档
  • 本期亮点

    • ❤️️️️ 根据文件名获取 MIMEContent-Type 类型
查看变化
var success = FS.TryGetContentType("image.png", out var contentType);  // image/png
  • ❤️️️️ 支持 Swagger 配置登录后才能访问
{
"SpecificationDocumentSettings": {
"LoginInfo": {
"Enabled": true,
"CheckUrl": "检查登录地址",
"SubmitUrl": "提交登录地址"
}
}
}

查看详细文档

  • ❤️️️️ 支持代码注释继承,Swagger 文档注释也支持
查看变化
/// <inheritdoc cref="ITestInheritdoc" />
public class TestInheritdoc : ITestInheritdoc, IDynamicApiController
{
/// <inheritdoc cref="ITestInheritdoc.GetName"/>
public string GetName()
{
return "Furion";
}

/// <inheritdoc />
public string GetVersion()
{
return "3.3.3";
}
}

/// <summary>
/// 测试注释继承
/// </summary>
public interface ITestInheritdoc
{
/// <summary>
/// 获取名称
/// </summary>
/// <returns></returns>
string GetName();

/// <summary>
/// 获取版本
/// </summary>
/// <returns></returns>
string GetVersion();
}

查看详细文档


v3.2.0(已发布)

  • 新特性

    •   IFormFile 拓展方法 ToByteArray() da69640
    •   规范化文档 ServeDir 虚拟目录配置功能,支持一键将一级目录切换至二级目录部署(IIS)8718392
  • 突破性变化

    •   所有依赖包至最新版
    •   依赖注入模块核心代码,移除注册服务采用反射机制,减少反射性能损耗 acdb315
  • 问题修复

    •   Swaggerschema 类型如果是 C# Object 类型无法正确生成前端代码问题 Swagger 官方 Issue 1a25274
    •   Worker Service 发布成 Windows Services 时日志绝对路径问题 感谢 @jacoat !467
    •   NginxIIS 对二级虚拟目录配置不同导致 404 问题 8718392
    •   远程请求模块未初始化 OnRequestFailded 导致空异常问题 #I54PK7
    •   依赖注入反射出现 Not found Method bug #I546L1
  • 其他更改

    •   定时任务失败后异常处理逻辑,感谢 @程小胜 !463
  • 文档

    •   定时任务文档,日志文档
    •   文件上传/下载 文档,包含单文件/多文件/Base64/Byte[]

v3.1.0(已发布)

  • 新特性

    •   远程请求模块异常 Http 状态码 !462
    •   动态 WebAPI 支持小驼峰配置 #I4W1R4
    •   远程请求 SendAsByteArrayAsync 等一系列方法,支持返回 byte[] !452
    •   远程请求 GZip 压缩支持 #I506S5
  • 突破性变化

    •   .NET6 依赖包全部升级至 NuGet 最新版 v6.0.3
  • 问题修复

    •   .NET6 WebApplication 模式二级虚拟目录问题 #I4UZLM #I4PZ0C
    •   日期验证不支持 2022-03-01 0:00:00(现在支持小时域 000) 问题 #I4Y3NT
    •   环境配置和文件配置优先级问题
    •   脱敏模块替换敏感词汇只替换最后一个 bug #I4YFA0
    •   远程请求返回字符串个别情况出现中文乱码问题 #I50GBD
    •   [DataValidate] 配置 AllowNullValueAllowEmptyString 无效问题 #I4ZZBE
  • 其他更改

    • [过时] 标记 Furion.Extras.Logging.Serilog 拓展包 IWebHost 拓展为过时状态
  • 文档

    •   优化文档体验,新增面包屑导航,重写文档缓存,提升文档访问速度
    •   更新动态 API 文档、配置文档、远程请求文档
    •   更新二级虚拟目录文档
  • 本期亮点

  1. 新增动态 WebApi 支持小驼峰路径,如 GetMyName -> getMyName
查看变化
{
"DynamicApiControllerSettings": {
"LowercaseRoute": false,
"KeepName": true,
"AsLowerCamelCase": true
}
}
  1. 支持 .NET6 WebApplication 模式二级虚拟目录配置:
查看变化
Progame.cs
app.UseVirtualPath(app =>
{
app.UseInject(String.Empty); // 注意 String.Empty 只是例子,可以不填或填其他的,见一分钟入门
app.MapRouteControllers();
});

v3.0.0(已发布,.NET6)

v3+ 版本说明

Furion v3.x 版本采用 .NET6 构建。

  • 新特性

    •   远程请求支持 GET 请求自动转换 类类型 类型对象 #I4HR5Q
  • 突破性变化

    •   全面支持 .NET6 版本
  • 问题修复

    •   开启规范化结果并自定义全局异常导致异常经过 OnSucceeded 过滤器 bug #I4DTVL
    •   .NET5.0.5+.NET6 微软底层修改了 [ApiController] 验证失败返回 IActionResult 类型 #I4ISOK
    •   EFCore 6.0 适配 SqlServer 2005+ 出错 #I4ILA5
    •   .NET6 获取配置对象如果不存在返回 null 问题,.NET5 则返回初始对象 94ae4d
    •   Sql 命令参数传入 Clay 类型异常问题 #I4D21Q
    •   Cron 定时任务特性方式 bug #I4OJQI
  • 其他更改

    •   ToPagedList 泛型约束 d0244d
  • 文档

  • 特别鸣谢


v2.20(已发布,全新事件总线)

v2.20+ 版本说明

Furion v2.20+ 版本后采用 Jaina 事件总线替换原有的 EventBus

  • 新特性

    •   远程请求支持 GET 请求自动转换 类类型 类型对象 #I4HR5Q
  • 突破性变化

    •   EventBus 模块,采用 Jaina 方式
  • 问题修复

    •   开启规范化结果并自定义全局异常导致异常经过 OnSucceeded 过滤器 bug #I4DTVL
    •   .NET5.0.5+ 微软底层修改了 [ApiController] 验证失败返回 IActionResult 类型 #I4ISOK
    •   远程请求上传文件异常 0c0752
    •   框架启动不支持环境变量 ASPNETCORE_HOSTINGSTARTUPASSEMBLIES 配置 !438
    •   定时任务内存和 CPU 占用及特殊情况下空异常问题 12c65de
    •   默认控制器启用规范化结果无效 bugc7a4a5e
    •   依赖注入 InjectionAttribute 特性的 ExceptInterfaces 单词拼写错误问题 !436
    •   Sql 命令参数传入 Clay 类型异常问题 #I4D21Q
  • 其他更改

    •   InjectionAttribute 代码 !435
    •   ToPagedList 泛型约束 d0244d
  • 文档

    •   事件总线新文档

v2.19(已发布)

  • 新特性

    •   定时任务监听器 ISpareTimeListener #I468Q1
    •   执行 Sql 支持 JsonElement 参数 61985d6
    •   Swagger 配置枚举及标签排序过滤器 #I46LON !404
    •   远程请求 application/octet-stream 类型默认支持 d9bad03
    •   远程请求代理模式请求报文头支持 IDictionary<string ,object> 类型。0204c0a
    •   MongoDB 拓展类,添加更多常用操作方法 !423
    •   DateTimeOffset? 转换 DateTime 拓展(包含互换) !432
  • 问题修复

    •   Scoped.CreateEFCore 进行 Add-Migration 时候报空异常问题,原因是在 PM 环境中不存在根服务0853e74
    •   定时任务执行异常后异常一直驻留内存问题,修正为执行成功自动清空过去异常 197a62b
    •   Jwt 拓展包不正确的代码导致 IOptions 失效#I46LUP
    •   Swagger 枚举 Schema 过滤器不输出值问题 #I46LON !404
    •   Swagger 处理非 int 类型枚举转换 bug #I46QJ9
    •   视图引擎编译模板生成 dll 后再次加载出现 IL 格式化错误问题 ff52d38
    •   管道 Channel 读取器无法释放 Handler 对象问题 10f4a90
    •   Worker Services 下日志不输出问题 c482548
    •   远程请求 multipart/form-data 类型对接微信小程序上传文件 问题 d9bad03
    •   工作单元上下文在某些情况下共享事务失效问题 006d439
    •   Swagger 枚举值在 GET 请求中 Schema 显示不正确问题 fb72fd7
    •   远程请求 404 不走异常过滤器问题 !426
    •   自定义事件总线并发情况下调用完成后无法正确处理队列数据 !429
    •   v2.19+ 版本之后模块化开发加载外部程序集失效问题 !433
    •   定时任务 SpareTime 频繁检查导致 CPU 增高问题 aa0a2ee
  • 特别鸣谢


v2.18(已发布)

  • 新特性

    •   Furion.Tools.CommandLine 拓展库 查看源码
    •   基于 AsyncLocal<T>CallContext 实现 9057a21
    •   远程请求可配置请求异常重试策略 656da87
    •   远程请求 OnRequestFailded 事件 4a3da4b
  • 突破性变化

    •   Scoped 所有带返回值方法 656da87 -   ConfigureService 中调用 App.GetOptions<>() 获取配置逻辑 afa4ac3

在过去,很多开发者总是喜欢在 Startup.cs 配置服务的 ConfigureService 方法中解析服务,这样导致内存存在溢出风险,GC 无法回收。 正确的方式是尽可能的避免 ConfigureService 中解析服务。如果需要在【启动时】获取 配置选项,请使用 App.GetConfig<TOptions>(路径, true) 代替 App.GetOptions<TOption>

  • 问题修复

    •   v2.16+ 版本重构 AppDbContextBuilder 之后写错实体类型 #I45E6M
    •   远程请求单个值序列化错误处理方式 3282eba
    •   v2.17.3+ 单元测试创建 TestServer bug #I45JR3
    •   Retry.Invoke 正常方法死循环 bug !392
    •   刷新 Token 生成新 Token 存在数组/集合类型导致 Key 重复异常问题 aeea2b1
    •   远程请求序列化引用类型对象(不含 string)不正确的处理 93cf63a
    •   AppDbContext 默认租户属性受工作单元影响问题 e51557f
  • 文档

    •   包管理工具文档
    •   模板引擎、Sql 操作,数据库上下文 等等文档

v2.17(已发布)

  • 新特性

    •   IPC(Inter-Process Communication,进程间通信) 模块功能,目前提供进程内通信和共享内存进程外通讯 ProcessChannel
    •   远程请求 application/xmltext/xml 默认支持 4753a1a
    •   控制台全局异常拦截 4a4fe1f
    •   支持自定义 .json 配置文件扫描目录 3e2910a
    •   支持数据库实体接口显式实现接口配置 9610a0a
    •   控制台应用程序全局拦截 [IfException] 支持 4a4fe1f
    •   依赖注入模块接口可以限制实现类生存周期,实现类也支持复写生存周期 d2ce089
  • 突破性变化

    •   Oops.Retry() 重试策略功能至新类:Retry.Invoke() 6a7bbd0
    •   IHttpContextAccessor.SigninToSwagger() 拓展,请使用 IHttpContextAccessor.HttpContext.SigninToSwagger(),退出也一样
    •   全局处理 Request Body 重复读处理 Request.EnableBuffering() d92c24b
  • 问题修复

    •   规范化状态码过滤逻辑错误问题 #I44JYS
    •   非关系型数据库(内存数据库)注册及操作异常 e167651
    •   远程请求默认序列化问题 a55603b
    •   定时任务零点/整点提前一秒触发问题 #I4321L
    •   友好异常在子类重写抽象类方法内部抛异常无法获取的问题 4a4fe1f
    •  Web 项目抛异常问题 4a4fe1f
    •   数据库实体模型贴 [NotMapper] 特性无效 #I44MNO
  • 其他更改

    •   Swagger 生成泛型 SchemaIds 默认连接符,由 Of 改为 _ 81946b6

v2.16(已发布)

  • 新特性

    •   MVC 控制器支持规范化处理 #I427Z2
    •   throw Oops.Bah() 抛出业务异常(状态码 400
    •   UnifyResultSettings 规范化 json 配置选项 #I42NY7
    •   多语言自定义配置资源文件名及自定义程序集 #I434YJ
  • 突破性变化

    •   规范化结果 IUnifyResultProvider 参数 (破坏性更改) #I427Z2
    •   IJsonSerializerProvider 接口参数,去掉 inherit 参数 a55603b
查看变化
新版本自定义规范化结果
using Furion.DataValidation;
using Furion.DependencyInjection;
using Furion.UnifyResult.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;

namespace Furion.UnifyResult
{
/// <summary>
/// RESTful 风格返回值
/// </summary>
[SuppressSniffer, UnifyModel(typeof(RESTfulResult<>))]
public class RESTfulResultProvider : IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, errors: metadata.Errors));
}

/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}

/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(StatusCodes.Status400BadRequest, errors: metadata.ValidationResult));
}

/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);

switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized")
, App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden")
, App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
default: break;
}
}

/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
private static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RESTfulResult<object>
{
StatusCode = statusCode,
Succeeded = succeeded,
Data = data,
Errors = errors,
Extras = UnifyContext.Take(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
}
  • 问题修复

    •   MVC 控制器启用规范化处理后返回 new Json({}) 对象为 null 问题 #I4354S
  • 其他更改

    •   多语言底层设计,取消需要创建 Lang.cs 空类的要求 #I434YJ
    •   MiniProfiler 性能,减少不必要的监听

v2.15(已发布)

  • 新特性

    •   Db.GetDbRepository<定位器>() 静态方法 #I41MZP
    •   远程请求缺省序列化配置选项 #I41PBW
  • 问题修复

    •   自动扫描接口进行依赖注入获取首个接口错误 bug #I41D1M
    •   IRepository<TEntity> 没有实现 IRepository<TEntity, TDbContextLocator> 问题 #I41MZP
    •   远程请求缺省序列化 bug #I41PBW
    •   AppDbContext.Tenant 多租户空异常问题 #I421DA
    •   Worker Service 多个 Worker 定时任务阻塞问题 82a79cc
    •   Jwt 自动刷新机制时区处理问题,主要针对国外用户 #I41UB1 82a79cc
  • 其他更改

    •   系统启动性能,从 106M 减少到 84M
    •   大量底层代码,包大小从 391Kb 减少到 350Kb(不带注释版本仅 64Kb)

v2.13/v2.14(已发布)

  • 新特性

    •   简易字符串模板功能,支持远程请求、数据库模块、日志模块、事件总线模块、定时任务模块、异常模块、数据校验模块 #I402BL
    •   404 状态码规范化默认处理 #I408F5
    •   定时任务 ISpareTimeWorker 声明方式支持异步方法 #I40KWR
    •   自动配置二级虚拟目录 !354
  • 突破性变化

    •   框架依赖 SDK.NET 5.0.8 版本
    •   Db.GetNewDbContext() 静态方法 #I400BK
    •   数据库模块时态表拓展支持 #I405HI
    •   IJsonSerializerProvider 接口参数,新增 inherit 参数 #I3ZQU5
    •   AppSettings 配置的 LogEntityFrameworkCoreSqlExecuteCommand 名称为 OutputOriginalSqlExecuteLog #I40VVE
  • 问题修复

    •   Worker Services 定时任务边界值问题导致跳过单次任务 #I405NI
    •   Worker Services 独立发布后程序集扫描失效 bug #I3ZH3X
    •   远程请求如果配置了 Client 客户端但传入了空 RequestUrl 地址导致异常问题 #I40BC6
    •   规范化结果篡改非短路端状态码出现异常 bug #I408F5
  • 其他更改

    •   App.GetServiceProvider(type) 解析服务性能 #I40KXN
    •   视图引擎保存成文件流默认缓存区大小,从 4096 提升至 8192 #I40KH5

v2.10/2.11/2.12 (已发布)

该版本有多个破坏性更改,更新时请认真查看。

  • 新特性

    •   App.Configuration.Reload() 拓展 #I3XYI8
    •   ISubscribeHandler 支持异步方法定义 #I3XYHJ
    •   app.UseUnifyResultStatusCodes() 可配置修改返回状态码 #I3VZQH
    •   远程请求添加默认 User-Agent#I3W17C
    •   支持 Sql 高级代理切换数据库上下文定位器 #I3XFP6 #I3XDCR
    •   定时任务 CronFormat 自动识别 #I3Y7GT
    •   Sql 高级代理 拦截功能 #I3YHG4
    •   拦截远程请求所有异常处理 #I3YPDE
    •   远程请求配置 Timeout 超时时间 #I3YPPK
    •   RSA 加密算法 #I3YZNU !345
    •   DataTableDataSet 支持不指定强类型返回 #I3Z6RI
    •   Sql 字符串拓展方法设置 Timeout 超时时间 #I3ZKWF
    •   Sql 高级代理 [Timeout] 特性,设置超时时间 #I3ZKWF
  • 突破性变化

    •   FakeDelete 假删除/软删除所有功能 #I3XKII
    •   PBKDF2 加密算法 #I3Z0IO
    •   远程请求设置超时时间单位由 分钟 改为 #I3YPPK
    •   IJsonSerializerProvider 接口参数,新增 inherit 参数 #I3ZQU5
    •   [NonAutomatic] 特性名称为 [Manual] #I3XKKX
    •   [NotChangedListener] 特性名称为 [SuppressChangedListener] #I3XKLZ
    •   [ManualSaveChanges] 名称为 [ManualCommit] #I3XKNP
    •   DbContext.TenantIdQueryFilterExpression 名称为 DbContext.BuildTenantQueryFilter #I3XKTB
    •   [SkipScan] 名称为 [SuppressSniffer] #I3XN5N
    •   [SkipProxy] 名称为 [SuppressProxy] #I3XN7O
    •   Sql 执行,性能提升 20% #I3W33U
  • 问题修复

    •   动态 WebAPI 扫描控制器没有屏蔽没有注册的第三方控制器 #I3Y7TJ
    •   AppDbContext 设置 TablePrefix 无效: #I3Y57Q
    •   定时任务使用异步委托导致程序终止 bug #I3XVZ0
    •   事件总线一个 消息id 对应多个 Handler 只触发第一个#I3XYP0
    •   .ToPagedList() 分页方法传入小于或等于 0 的页码 #I3XNAN
    •   JSON 序列化默认 DateTimeOffset 异常 #I3XMOL
    •   继承 Serilog 日志在 Worker Service 生成重复日志 bug #I3WA0L !331
    •   粘土对象 动态添加 Clay 类型 bug #I3W9LW
    •   ValidationTypes.Numeric 校验数值类型正则表达式错误 #I3WADS
    •   数据库命令参数 DbParameterValueobject 类型的时候且不指定 #I3YKM6
    •   Oracle 数据库存储过程 游标参数 报错问题 #I3ZBYE
    •   Worker Services 采用独立发布后无法执行问题 #I3ZH3X
    •   远程请求如果无返回值序列化异常问题 !348
  • 其他更改

    •   支持 appsettings.json 等自定义配置文件中文命名 #I3YBFD
    •   远程请求配置命名客户端 BaseAddress 地址兼容处理 #I3YCRH
    •   框架无用代码、优化代码
    •   Furion非 Web 环境下性能
  • 文档变化

    •   会话和状态管理 文档 #I3YI3G
    •   远程请求、日志、数据库上下文、远程请求、Sql 高级代理文档
    •   配置文件 #I3Y2EV
  • 问答答疑

    •   dapper 多个数据源如何继承 #I3WUOI
    •   关于 SpareTime 多次执行问题#I3XEQU
    •   选项更改通知(热更新):数据库里的数据更改了如何通知选项进行改变? #I3XYI8
    •   SaaS 多租户添加时无法获取租户Id #I3Y5CF
    •   获取 _httpContextAccessor.HttpContext 为空#I3Y6BI
    •   Ubuntu 中使用 App.Configuration 方法读取不到值 #I3Y74H
    •   数据库上下文作用域问题 #I3YHXP
    •   使用 UnitofWork 提交事务,可以提交成功,但是系统会有错误 #I3YIWU
    •   数据库读写分离--非默认主库的从库随机该如何配置? #I3YVR7

v2.9.0 (已发布)

  • 新特性

    •   应用全局未托管资源监听,并实现特定时机释放非托管资源 #I3VXAU
    •   不包含 EntityFramework.Core 版本的 Furion.Pure#I3VGW8
    •   swagger 支持设置多语言方式,设置的语言自动添加到 api 地址后面 #I3VDTD
    •   动态 WebAPI 支持 [FromRoute] 非必填(选填)参数设置 #I3VFIM
    •   动态 WebAPI 参数支持配置路由约束 #I3VFIR
    •   MD5DESC 加密支持 大写 输出 #326
  • 突破性变化

    •   Furion 所有包生成 .snupkg 包,支持开发阶段直接调试 Furion 所有包源码 #I3VFIX
    •   repository.BuildChange() 方法的返回值,多返回一个 IServiceScope 对象 #I3VX3D
    •   JWT 刷新 Token 方法 AutoRefreshToken 参数 days 改为 minutes #I3VXNB
  • 问题修复

    •   App.GetOptionsSnapshot<> 从根服务解析异常 bug #I3VS2X
    •   远程请求如果出现异常,返回 Stream 为 null 导致异常的问题 #I3VSTU
    •   如果实体被跟踪后,无法执行删除操作 #I3W08P
  • 其他更改

    •   运行时内存,实现请求结束自动释放未托管资源 #I3VXAU
  • 文档变化

    •   App 静态类文档、远程请求文档、分表分库文档
  • 问答答疑

    •   动态 WebAPI,自定义根据方法名生成 [HttpMethod] 规则报错 #I3VKQG
    •   InsertAsync 的时候提示 ID 为空 #I3VS7E
    •   FirstOrDefault 自动过滤了 TanantId 字段 #I3W0VH
    •   对方接口返回 HttpConnectionResponseContent 远程请求拿不到返回值 #I3W17C
    •   查询方法 FindOrDefault 报错 #I3W830
    •   SqlNonQueryUnitOfWork 循环执行#I3W8WW
    •  Swagger 配置问题,导致 Swagger 中不能自动携带 token 授权的问题 #I3W934
    •   远程请求 SetBody 参数识别不了#I3WBM1
    •   Scoped.Create 里执行 sql.SqlNonQuery() 或者 obj.insert() 问题#I3WB5O
    •   调用函数或存储过程,怎么出参数据自定义对象?如 Oracle数据库的数组或记录 #I3W71W

v2.7.0/2.8.0 (已发布)

  • 新特性

    •   throw Oops.On("异常消息") 应用多语言支持 #I3UYC2
    •   Db.GetMSRepository() 获取主从库仓储静态方法 #I3UBSJ
    •   工作单元特性,支持静态类强制性开启共享事务 #I3S9N8
    •   EFCore 执行 sql 模式打印日志 #I3SE8X
    •   远程请求支持默认 HttpClient 配置 #I3SI17
    •   短 ID 生成功能 #I3T7JP
    •   [SensitiveDetection] 支持配置替换敏感词汇 #I3THIA
    •   SpecificationDocumentBuilder.DocumentGroupsSpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(currentGroup, apiDescription) 公开方法#I3UDSY
  • 突破性变化

    •   自动扫描 .json.xml 文件并加载到配置中的代码和规则,同时移除默认 .xml 文件加载,只保留 .json 文件 #I3UJ3L
    •   分布式连续 GUID 代码 #I3UBK0
    •   Scoped.CreateUnitOfWork 名称为 Scoped.CreateUow #I3SJPU
    •   JWTEncryption.Validate 返回值,支持返回 TokenValidationResult #I3S2ND
  • 问题修复

    •   [DataValidation][SensitiveDetection] 多语言应用失效 #I3UH6U
    •   Scoped 系列方法异步出现 Task is cancel 情况 #I3SJF6
    •   Mysql 数据库的 ToPagedList 方法返回的结果进行遍历出现 MySqlConnection is aleady use 问题 #I3SJQ3
    •   tool/cli.psl 没有包含项目名称 #I3S1T6
    •   远程请求做上传文件时,没有传入 Body,程序直接跳过 #I3TKFH
    •   远程请求 multipart/form-data 内容分割符缺失 #I3TNO9
    •   远程请求代理拦截方式返回 HttpResponseMessage 问题 #I3V161
    •   repository.Database.SetCommandTimeout(600) 无法生效#I3VAQS
  • 其他更改

    •   支持规范化结果中间件判断是否跳过规范化结果 #I3T2AA
    •   更新部分列 UpdateIncludeNowAsync 具有二义性 #I3RW9Q
    •   框架底层性能,大大减少内存占用和溢出情况,启动内存从之前 136M 下将到 86M
    •   删除无用代码,优化不规范命名等
  • 文档变化

    •   Inject 说明文档 #I3TITA
    •   4.2.9 的示例代码文档,方法没有放在 class 中 #I3S9T5
    •   规范化结果 6.5.6 多分组排序图片引用错误 #I3UBOQ
    •   静态类 Scoped 文档
  • 问答答疑

    •   默认 MasterDbContextLocator 不随自定义的参数生成 #I3SDBB
    •   事件总线中订阅处理程序类获取不到用户信息,这个正常吗 #I3SS0U
    •   在有多租户过滤器的情况下,是否有一种方式查询全量的数据 #I3T0VI
    •   mysql 使用 &"tools/cli.ps1" 页面化加载表结构失败 #I3T4F8
    •   其他 Web 层的 Startup 优先执行 #I3T8IP
    •   辅助角色服务实现建议 #I3T906
    •   开启 easy connection 后同一内网地址浏览器可以正常访问,远程请求则无法访问#I3TA2U
    •   scope.ServiceProvider.GetService<IOtherService>不存在 #I3TQMV
    •   能否在 WPF 项目中使用呢? #I3TMCC
    •   Dapper 多个数据源 #I3TM9B
    •   L.GetSelectCulture() 方法异常 #I3TQS4
    •   循环中使用 IDGen.NextID() 得到的结果并不是连续的 #I3UAF6
    •   模块化动态加载插件支持通配符匹配.dll #I3UDT8
    •   MVC 模式,在 Controller 里快捷方式创建 View 页面出错 #I3UFGB
    •   数据库迁移没有种子数据 #I3UI7G
    •   SpareTimeAttribute 中 根据 Cron 表达式 自动匹配 Cron 表达式格式化方式 #I3UTKQ
    •   使用 workService 集成 SqlSugar 报错 #I3V8HJ
    •   sqlserver 2008 分页报错如何解决呢 #I3VF96

v2.5.0/2.6.0 (已发布)

  • 新特性

    •   虚拟文件服务,支持物理文件和嵌入资源文件 #I3RBR9
    •   读写分离/主从复制仓储 IMSRepositoryIMSRepository<TMasterDbContextLocator> 仓储,可进行随机或自定义获取从库
    •   数据脱敏处理 #I3R5ZF
  • 突破性变化

    •   InsertOrUpdate 一系列数据库操作方法 #I3RI9L
    •   所有包含 Exists 单词的数据库操作方法 #I3RJ0T
    •   分布式 GUID IDGenerater 静态类名称为 IDGen #I3RGUA
  • 问题修复

    •   远程调用方法错误,请求报文头 Headers 不能添加到 IHttpDispatchProxy 的子接口上 #I3RAF7
  • 其他更改

    •   应用启动性能,减少内存分配
  • 文档变化

    •   脱敏处理文档 #I3R6WZ
    •   文件系统文档、FS 静态类文档 #I3RCC4
    •   读写分离/主从复制、数据库仓储文档、Db 静态类 #I3R3B6
  • 问答答疑

    •   关于 Furion 集群部署 #I3R3J4
    •   升级最新框架以后, 数据库生成模型报错 #I3R7TP
    •   数据库上下文事务执行中,SaveNow 执行后有警告 #I3RAJI
    •   Hangfire 使用事务出现错误 #I3ROQ5
    •   如何实现 cli 不执行某些表的迁移,web 请求可以正常操作呢? #I3ROU5
    •   在使用定时任务时候出现的问题:继承 ISpareTimeWorker #I3RRZS
    •   MySql 时间差 8 小时处理 #I3RSCO
    •   Db.GetRepository<> 方法结合 [UnitOfWork] 后不可用 #I3RUK5
    •   事务开启失败问题 #I3RYJY
    •   支持 DbProvider 可动态配置 #I3RYPE
    •   WorkService 依赖注入 ISingleton 问题 #I3RZ1L
    •   ISpareTimeWorker 运行期动态修改 #I3S33Q

v2.4.0 (已发布)

  • 新特性

    •   支持自动加载模块化/插件 .xml 注释文件 #I3Q7XY
    •   AppDbContext.FailedAutoRollback 属性,可配置事务是否自动回滚 #I3QOUS
  • 突破性变化

    •   .NET 5 SDK 为 5.0.6 版本
    •   IJsonSerializerProvider.GetSerializerOptions() 接口方法 #I3QIJN
  • 问题修复

    •   通过 services.AddInject() 方式注册,模块化/插件不加载 #I3Q7XH
    •   种子数据返回 null 报空异常 #I3QCM5
    •   通过 Clay.Object 创建粘土对象后属性变小写问题 #I3QRV3
  • 其他更改

    •   Furion 框架底层性能,减少内存占用,提高应用初始化速度 92f8cc1
  • 文档变化

    •   JSON 序列化文档、规范化结果文档、数据库上下文文档
  • 问答答疑

    •   InsertOrUpdateNowAsync 报错 #I3QKO5

v2.3.0 (已发布)

  • 新特性

    •   Furion.Extras.DatabaseAccessor.MongoDB 拓展包支持 #I3PKST
    •   动态粘土类型直接转 objectdynamic 类型 #I3OY27
    •   Oops.Retry 方法,支持设置方法调用失败进行重试 #I3PJKQ
    •   JWTSettings 配置节点 Algorithm,用于配置加密算法 #I3PQGV
    •   repository.EnsureTransaction() 方法确保工作单元事务有效 #I3PVF1
  • 突破性变化

    •   支持 .NET 6.0.0 Preview 3 版本 #I3P2C7
  • 问题修复

    •   使用数据库生成模型 tools/cli.ps1,从数据库表生成的实体异常 #I3PL18
    •   贴了 [NonUntify] 特性后,SwaggerExample Value 没有匹配正确 #I3PK0L
    •   SpareTimer.TallyCron 表达式中计数无效 #I3PWSE
  • 其他更改

    •   框架默认序列化应该从配置中读取,而非手动编写 #I3P1SJ
    •   SqlSugar 拓展库,支持非泛型仓储获取上下文操作对象 #I3PK2N
    •   支持分布式内存缓存可配置化 #I3POKD
  • 文档变化

    •   添加 JWTSettings 配置独立文档 #I3PQGW
  • 问答答疑

    •   软删除如果数据不存在,则报错 #I3PTVB
    •   多个类集成测试会造成数据库定位器多次注册,无法运行所有测试,只能一个类一个类的运行 #I3PXGY

v2.2.0 (已发布)

  • 新特性

    •   Clay 粘土类型,支持让 C# 创建一个弱类型对象并操作弱类型 #I3O2QQ
    •   Scoped.Create 带返回值重载 #I3O47J
    •   支持 Scoped.Create() 一系列方法支持传入作用域工厂 #I3OAP5
    •   支持事件总线同步执行方式 #I3OAW2
    •   [DataValidation] 跳过空字符串和空值验证 #I3OGEN
    •   Worker Service 可配置是否自动注册 Worker #I3OLW4
  • 突破性变化

  • 问题修复

    •   定时任务设置 cancelInNoneNextTime: false 一次也不执行 #I3O3N0
    •   SpareTime 自定义下次执行时间出现空异常 #I3O46X
    •   MiniProfiler 设置为 false 时,数据库上下文提交拦截器未添加 #I3OAWX
    •   [Consumes("application/x-www-form-urlencoded")]ModelQuery 配置同时配置导致空引用问题 #I3ODUR
    •   在 Grpc 中使用 jwt 授权出现空异常 #I3OW3I
  • 其他更改

    •   支持发布后代码精简配置,减少不必要的文件夹输出 #I3OAPF
    •   自动刷新 Token 机制,新增容错值处理,解决并发 Token 刷新失败问题 #I3OGYF
  • 文档变化

    •   粘土对象文档 #I3OG18
  • 问答答疑

    •   动态 WebAPI 如何获取接收文件 #I3O29B
    •   定时任务使用 Scope.CreateUow 引发的问题 #I3O2CD
    •   单文件发布程序工作不正常 #I3O4D8
    •   同时配置租户过滤器和软删除过滤器,最终的 sql 只生成了一种过滤条件 #I3OB0A
    •   HTTP 重定向 HTTPS 后跨域失效 #I3OB8R
    •   在 PostgreSql 数据库使用 rep.FirstOrDefault(u => u.Id == UserId); 引起异常 #I3O5OF
    •   定时任务有时能触发有时不能触发 #I3ORBE

v2.1.0 (已发布)

  • 新特性

    •   新增定时任务 ISpareTimeWorker 方式支持 [SpareTime("{配置路径}}] 方式 #I3NTUX
    •   定时任务支持异步委托 #I3NP96
    •   轻量级分布式连续 GUID 生成器 #I3NKLZ
    •   ClayObject 模块,处理 ExpandoObjectIDictionary<string,object> 类型 #I3N3J4
    •   Scoped.CreateUow(handler) 创建作用域并自动提交数据库更改方法 #I3NU3G
  • 突破性变化

    •   规范化结果接口 OnResponseStatusCodes 方法,新增 UnifyResultStatusCodesOptions 参数 #I3NDB9
    •   雪花 ID 实现代码 #I3NKLZ
  • 问题修复

    •   Swagger 不能支持非 int 类型的枚举 #I3NQM8
    •   数据库线程池多线程并发问题 #I3NR4L
    •   自定义控制器路由后且为方法参数指定了 [ApiSeat] 后生成路由重复 #I3NRF6
  • 其他更改

    •   支持应用启动的时候迁移种子数据 #I3NH3M
  • 文档变化

    •   分布式 ID 生成文档 #I3B6CX
    •   新增模块化开发文档 #I3NSUS
    •   20.4 字符串拓展方式 > 错误ToAESDecrypt 写成了 ToToAESDecrypt #
  • 问答答疑

    •   有关【定时任务/委托】的疑问 #I3N3EW
    •   统一返回格式支持自定义 #I3NU1G

v2.0.0 (已发布)

  • 新特性

    •   控制台应用程序及 Worker Services 支持 #I3K4DG
    •   完整任务调度功能 #I3IRUX
    •   Cron 表达式解析 #I3IQ9Y
    •   支持 Swagger 自定义配置 swagger.json 地址模板 #I3IHMX
    •   支持配置动态 WebApi 区域 #I3IJAZ
    •   远程请求新增支持传入服务提供器 IServiceProvider #I3IVBL
    •   全局配置选型 SupportPackageNamePrefixs 配置,支持配置包前缀 #I3K0SN
    •   应用启动时支持 referenceassembly 类型程序集扫描 #I3K0SN
    •   依赖注入 AOP 拦截获取方法真实特性 #I3LZBX
    •   EFCore 手动 SaveChanges() 特性 #I3N01Y
    •   支持 Cors 跨域更多配置 #I3N2J0
  • 突破性变化

    •   完整任务调度功能 #I3IRUX
    •   日志模块功能 #I3J2K0
    •   模板引擎功能 #I3J46E
    •   底层 EFCoreRepository 仓储 #I3J6W5
    •   sql 字符串拓展底层代码 #I3IVCE
    •   底层 SqlRepository 所有逻辑代码 #I3J6V6
    •   数据库实体拓展方法 #I3J609
    •   事件事件总线同步执行为异步方式执行 #I3J0WA
    •   框架底层 HttpContext.IsAjaxRequest() 拓展 #I3IVAA
    •   ValidationTypes.Required 验证 #I3KR85
  • 问题修复

    •   关闭 InjectMiniProfiler 参数后内存缓存无效 #I3IHLR
    •   在多租户中调用 Tenant 属性出现偶然性数据库上下文被释放的情况 #I3IC70
    •   Sql 代理中如果返回基元类型抛出不能将 object 转换成对应类型的异常 #I3IC84
    •   存储过程多返回值的时候,outputvalues 的 name 不是定义的 MSG 的 name,是 Msg 类型。 #I3IC7Y
    •   PhoneNumber 手机号验证正则表达式错误 #I3ID10
    •   依赖注入 AOP 拦截无法捕获内部异常 #I3IGCC
    •   全局拦截标记异常已被处理后异常过滤器依然执行 #I3J463
    •   自定义全局异常拦截器不起作用 #I3K1SJ
    •   在 WorkerService 模式下,还是使用 WebHostEnvironment 来判断 Host 环境,会导致错误 #I3LCQY
    •   定时任务 DoOnce 抛空异常 bug #I3M0ZT
  • 其他更改

    •   启动时程序集扫描类型 #I3K0SN
    •   App.GetConfig<>("key") 不支持获取单个值问题 #I3ILF1
    •   UrlEncode 应该用 Uri.EscapeDataString() 而不是 HttpUtility.UrlEncode #I3ICTK
  • 文档变化

    •   定位任务、后台任务文档 #I3JHHG
    •   辅助角色服务文档 #I3K5GN
    •   动态 WebAPI、规范化文档、数据库上下文文档
  • 问答答疑

    •   数据校验,自定义 ErrorMessage 无效问题 #I3ICL3
    •   最新 issue 中新增的“新增常用的 JSON 序列化方法” 会导致 AOP 拦截异常 #I3I7VE
    •   Furion.DatabaseAccessor.PrivateEntityBase 中的 TenantId 数据类型设置为 object #I3IQV6
    •   有关异常拦截和处理的疑问 #I3IUFZ
    •   DataValidation 在空值的情况下被忽略掉了#I3IWSM
    •   日志文档没有更新 #I3J1DX
    •   对于 webapi 简单类型参数,是否可以以 json 方式提交 #I3J18I
    •   IUnifyResultProvider 实现中如果 UnifyModel 的 type 不是范型会报错 #I3JBXF
    •   如何模块化开发新功能? #I3J7ZZ
    •   建议增加微服务中间件的集成 #I3JTZQ
    •   二级虚拟目录部署的 swagger 的 MiniProfiler js 报错 #I3IWLR

v1.19.0 (已发布)

  • 新特性

    •   EFCore 5.0 支持 SqlServer 2005-2008 数据库 #I3HZZ6
    •   Sql 高级代理支持模板替换了 #I3HHWU #I3HH2T
    •   PBKDF2 加密 #I3HN7A
    •   常用的 JSON 操作方法 #I3HUYO
    •   所有解析服务的方法都支持传入 IServiceProvidier 参数 #I3HXEU
  • 突破性变化

    •   .NET 5 SDK 至 5.0.5 版本
  • 问题修复

    •   远程请求 application/x-www-form-urlencoded 自动被转码了 #I3HDPC
    •   ISqlDispatchProxy 调用带返回值的存储过程出错 #I3HISS
    •   多数据库工作单元异常无法回滚数据 #I3I2KN #I3HYN5
    •   Serilog 日志生成太多文件 #I3I2PN
    •   1.18.0 版本数据库连接池存在连接泄漏问题 #I3I5KO
    •   Sqlite 提示事务已完成异常 #I3I9F2
  • 其他更改

    •   视图模板功能,默认支持可枚举泛型类型 #I3GYEE
    •   开发阶段 MiniProfiler 打印数据库相关信息 #I3I8VQ
    •   EFCore 5.0 未提供 Sqlite 数据库 DataAdapter 的支持 #I3I9FC
  • 文档变化

    •   数据库上下文、多租户、仓储、日志、序列化等文档。
  • 问答答疑

    •   建议 MVC 模式下增加 Furion 的功能 #I3GY4R
    •   数据库关联操作 #I3H5QP
    •   1.17.5 版本 suagger 无法生成 swagger.json #I3HGPZ
    •   Serilog 扩展+dll 启动与 swagger 的 MiniProfiler 冲突 #I3HWJM
    •   Sql 高级代理返回 DataTable 时,结果为空取不到记录 #I3HUWG
    •   Task.Run 操作数据库问题 #I3HZ9D

v1.18.0 (已发布)

  • 新特性

    •   Oracle 11 版本支持 #I3EVL5
    •   Mysql 官方包 MySql.EntityFrameworkCore 支持 #I3E6J1
    •   全局配置 WebApi 参数 [FromQury]#I3EFYJ
    •   公开框架底层依赖注入扫描注册拓展 services.AddRisterTypes(types) #I3EIV3
    •   SqlSugar 工作单元特性 #I3EJO5
  • 突破性变化

  • 问题修复

    •   数据库上下文池一旦有上下文操作失败还数据库上下文出现二次提交数据库的问题 #I3EIJJ
    •   不同数据库命令参数前缀都添加了 @ 处理 #I3EBJP
    •   尝试修复事件总线线程安全问题 #I3EGSB #PR236
    •   HttpContextExtensionsSignoutToSwagger 方法无效 #I3EHNQ
    •   如果动态 WebApi 贴了 [ApiController] 特性后,导致路由参数重复生成 #I3EOQQ
    •   如果没有任何 webapi 控制器时,文档报错 #I3EVLB
    •   依赖注入泛型类型注册失败 #I3EX66
  • 其他更改

    •   SqlSugar 拓展库仓储 Context 属性类型未 SqlSugarClient #I3EHXA
    •   刷新 Token 黑名单 Redis 中分组 #I3EQWO
    •   远程请求在请求拦截次发起二次请求导致异常问题 #I3ER71
    •   多租户默认缓存改为分布式缓存 #I3EXEU
  • 文档变化

    •   数据库操作文档 #I3E84X
  • 问答答疑

    •   如何方便的获取 IDynamicApiController API 产生的 url 和 谓词 #I3ED17
    •   Code First -执行命令 Add-Migration 遇到了问题 #I3EHD0
    •   tools v1.16.0 无法生成实体,一直提示 Missing required argument <PROVIDER>. #I3ENZ8
    •   Authorize 的 Logout 按钮,无法实时请空 token#I3EOF9

v1.17.0 (已发布)

  • 新特性

    •   动态 WebAPI 支持继承基类配置特性 #I3D5PX
    •   远程请求支持 multipart/form-data 内容类型处理 #I3D7KG
    •   字符串加密拓展 #I3DHBW
    •   新增远程请求可直接下载返回值内容转为 string 类型 #I3DIGR
    •   远程请求地址支持模板引擎 #I3D5Y8
    •   [DataValidation] 错误消息支持 string.Format 操作 #I3E08W
    •   远程请求 HttpRequestMessage 拓展方法 AppendQueries() 追加更多 query 参数拓展 #I3E3DI
  • 突破性变化

    •   IRepository.AsAsyncEnumerable() 返回值 #I3DIQ1,调整为:rep.AsQueryable().ToListAsync()
  • 问题修复

    •   数据验证失败后也打印了成功的字段 #I3CVBS
    •   远程请求配置 contentTypeapplication/x-www-form-urlencoded 无效问题#I3CWBS
    •   远程请求无法打印完整的请求地址,比如配置了 HttpClient 之后 #I3CY42
    •   程序启动时排除默认配置文件算法不对,应该采用正则表达式匹配 #I3D9E7
    •   远程请求成功请求拦截不生效 #I3DOE4
    •   Dapper 拓展数据库切换为 oracle 时,系统找不到指定的文件 Oracle.ManagedDataAccess.Core #I3DYM3
  • 其他更改

    •   获取 JWT token 信息支持配置 Token 前缀,如 Bearer #I3DJIV
    •   刷新 Token 黑名单存储方式,将内存缓存调整为分布式缓存 #I3DPBR
  • 文档变化

    •   远程请求文档 #I3CPJO
  • 问答答疑

    •   LinqExpression.And 没有 2 个参数的方法 #I3CXKZ
    •   异常信息 如何记录到数据库中:) #I3DDGO
    •   无键实体选用 IEntityNotKey #I3DWRF
    •   根据主键删除一条记录不成功,无错误信息 #I3DWWF
    •   如何自定义接口返回格式 #I3DZN6
    •   DynamicApiController 如何在运行时决定是否公开一个 Action #I3D5UL
    •   Furion.DatabaseAccessor.DbHelpers 方法:ConvertToDbParameters 是不是应该过滤掉贴 NotMapped 的特性 #I3E2XS

v1.16.0 (已发布)

  • 新特性

    •   IDGenerator 雪花 ID 算法,感谢 idgenerator 作者提交 PR #PR204 #I3B60S
    •   DbContext 刷新多租户缓存拓展方法 #I39N5U
    •   自定义配置单个控制器名称规范,如小写路由 #I3A5XL
    •   获取当前选择区域语言方法 #I3BSDH
  • 突破性变化

    •   .NET 5 SDK 至 5.0.4 版本 #I3ASTL
    •   远程请求所有功能 #I2LB7M
    •   JSON 序列化功能,提供统一的抽象接口,方便自由替换 JSON#I39GT9
    •   验证失败返回消息模型及规范化接口验证参数 #I3AFQW
    •   插件式开发热插拔功能,实现动态加载卸载 #PR200, 感谢 @SamWangCoder
    •   JsonSerializerUtility 静态类及移除属性大写序列化拓展配置 #I3AFRJ
  • 问题修复

    •   MVC 模式下不支持验证自定义验证逻辑 #I39LM5
    •   验证数值类型正则表达式不支持负数 bug #I39YUV
    •   框架启动时无法加载未被引用的程序集 bug #I3A3Z4
    •   EFCoreRepository.IsAttached() 方法判断错误 bug #I3A824
    •   动态API 驼峰显示配置无效 bug #I3AF32
    •   cli.ps1 不支持新版本 EFCore bug #I3APO9
    •   EFCore 实体配置 [Table] 特性无效 bug #I3BAYH
    •   动态 WebAPI CheckIsSplitCamelCase bug #I3BLKX
    •   动态 WebAPI 配置保留 Action 的 Async 后缀无效问题 #I3C3DA
    •   JWT Token 刷新后旧的刷新 Token 依旧可用 bug #I3C8ZH
    •   多语言 Razor 视图变量多语言乱码问题 #I3CBMU
  • 其他更改

    •   默认序列化提供器 System.Text.Json 反序列化字符串时区分大小写问题 #I3BSXV
    •   优化 MessageCenter 性能问题 #I39PRR
    •   数据库上下文池小性能优化
  • 文档变化

    •   Docker 环境下自动化部署 #PR209
    •   JSON 序列化 文档 #I3B6D8
    •   跨域、安全授权、即时通信文档、多语言、规范化文档
  • 问答答疑

    •   Furion.Extras.DatabaseAccessor.SqlSugar 配置多个数据库打印 SQL 语句问题 #I39PDC
    •   ORACLE 数据库多租户模式下返回值为指定类型时系统卡死 #I39RNH
    •   假删除指向异常 #I39XZA
    •   Furion 多语言配置节是放在 AppSettings 里面还是外面呢? #I3A4SB
    •   没找到数据库上下文 #I3A5HS
    •  QQ 交流群吗? #I3AAM7
    •   Vue3 环境下配置 SignalR 跨域出错 #I3ALQ7
    •   设置 Swagger 参数非必填 #I3AT02
    •   EFCore 调用 Insert 时报 Unknown column 'Discriminator' in 'field list' 异常 #I3B2LC
    •   逆向 mysql 数据库时 cli 出现错误 #I3B64F
    •   Sql 高级代理使用过程中 DateTime 类型的参数序列化失败 #I3AZXK
    •   使用 Mysql 执行 Add-Migration 报错 #I3B8EW
    •   Saas 多租户模式-独立 Database 模式下无法获取 Tenant, 导致无法自动切换的问题#I3AVXU
    •   如何自定义 WebAPI 统一结果模型 #I3BBYW #I3BBYV
    •  Web.Entry 项目新建了一个 Controller,多了未知方法 #I3BKH5
    •   AOP 拦截如何解析服务 #I3BUM3
    •   动态 WebAPI 返回参数被省略 #I3C2XR
    •   如何设置某一个接口响应数据不自动转小写,按原始字段名返回 #I38L9B
    •   code first 如何配置自动迁移 #I3CCR0
    •   webapi 混合授权如何区分不同系统 #I3CJCY
    •   EFCore 不支持递归无限级遍历关系 #I3CET9

v1.15.0 (已发布)

  • 新特性

    •   跳过特定实体数据库操作监听特性 #I386LB
    •   IEntityChangedListener 增加对 OldEntity 的支持 #I385X2
    •   实时通信自动配置集线器拓展及特性 #I387QX
    •   Mapster 拓展支持 IMapper 依赖注入方式 #I38C7C
    •   [AppDbContext] 特性默认构造函数 #I38J97
    •   UnifyContext.GetExceptionMetadata(context) 返回错误码支持 #I38ONX
  • 突破性变化

  • 问题修复

    •   多次循环中调用 Db.GetNewDbContext() 还是获取到同一个对象 #I38NNP
    •   Swagger 过滤掉 object 类型属性问题 #I38FHL
    •   同一类不支持多继承 IEntityChangedListener 问题 #I38UQJ
    •   自定义序列化属性名称导致验证失败属性不匹配问题 #I38W8Z
  • 其他更改

    •   代码不规范命名导致开发者阅读代码时产生歧义
  • 文档变化

    •   FluentValidation 集成文档 #I38IOT
  • 问答答疑

    •   Furion 框架版本向下兼容问题 #I38WMZ

v1.14.0(已发布)

  • 新特性

    •   EFCore 5.0 的 Oracle 数据库支持 #I37Z8E
    •   控制是否在开发环境下显示数据库连接信息 #I37YQ2
    •   [NonUnify] 支持在类中贴此特性 #I359Q6
    •   网络请求 字符串 HttpClient 拦截器 #I35F3E
    •   HttpContextHttpRequest 获取远程地址拓展 #I3688Z
    •   services.AddMvcFilter<> 添加 Mvc 过滤器拓展 #I368BH
  • 突破性变化

    •   框架依赖的 .NET 5 SDK 至最新版 5.0.3 #I37YQQ
    •   Swashbuckle.AspNetCore 组件包到 6.0.x 版本 #I37EZK
    •   Furion 框架 JWT 拓展类,只在 Furion.Extras.Authentication.JwtBearer 中保留 #I35D59
  • 问题修复

    •   传入错误 JWT Token 字符串导致自动刷新 Token 出现字符串边界值异常 bug #I34ZE5
    •   瞬时作用域数据库上下文也会自动加入工作单元导致写日志时连锁异常 bug #I37WTV
  • 其他更改

    •   获取系统环境参数的性能 #I36SR5
    •   Furion 底层添加 Mvc 过滤器代码 #I36SKA
    •   添加默认 Json 序列化时间默认时间格式 #I36SL0
    •   SqlSugar 拓展包到 5.0.2.6 版本 #I36SIG
  • 文档变化

    •   数据库入门文档 #I37Z8S
    •   更新日志文档 #I36PI0
    •   请求审计日志、执行 Sql 更新日志文档 #I36PIK
    •   前端使用 axios 跨域配置文档 #I36PIT
    •   App 静态类获取应用、环境更多信息数据 #I36SOV
    •   英文版 README.md 介绍 #I37QHP

v1.13.0(已发布)

  • 新特性

    •   多语言功能及拓展 #I2DOCL
    •   事件总线功能及消息中心 #I23BKN
    •   Swagger 分组显示隐藏配置 #I2AHH8
    •   Furion.Extras.Logging.Serilog 拓展插件 #I2AAN8
    •   cli.ps 支持 -Namespace 命名空间指定 #I2A175
    •   Swagger 规范化化文档授权失效后自动取消授权锁 #I2AIWC
    •   Request.Body 支持重复读功能,主要解决微信 SDK 问题 #I2AMG0
    •   网络请求功能及文档 #I2APGJ
    •   SqlSugar 拓展包支持打印 sqlMiniProfiler#I2ASLS
    •   Furion.Extras.DatabaseAccesssor.Dapper 拓展插件 #I2ASYA
    •   Furion.Extras.DatabaseAccessor.PetaPoco 拓展插件 #I2AUGA
    •   网络请求字符串拓展方法 #I2CPQ0
    •   SqlSugar 拓展新增 PagedList 拓展 #I2CW99
    •   远程请求支持参数特性验证 #I2CX5L
    •   App.User 获取当前授权用户信息便捷方法 #I2CZLO
    •   规范化文档可配置功能,支持 appsettings.json 配置 #I2D1K9
    •   远程请求拦截器添加方法和方法参数 #I2D2CM
    •   远程请求出错返回默认值支持 #I2D44M
    •   远程请求 body 参数序列化支持设置 PropertyNamingPolicy #I2D685
    •   远程服务接口客户端配置 #I2D7PS
    •   AddInjectUseInject 允许自定义 SecurityDefinitionsSwaggerUI #I2DIMG
    •   [SecurityDefine] 默认构造函数 #I2DNXT
    •   AspectDispatchProxy 动态代理类 #I2DO6I
    •   [QueryParameters] 特性,支持一键将 Action 参数添加 [FromQuery] 特性 #I2G8TF
    •   动态日志配置及拓展方法 #I2GDGD
    •   WebApi 请求谓词默认规则配置功能 #I2M70X
  • 突破性变化

    •   .NET 5 SDK 到 .NET 5.0.2 版本 #I2D0PZ
    •   框架内所有拓展类命名空间,全部迁移到 Furion.模块.Extensions#I2AH54
    •   Swagger 记住授权存储方式,替换 Session 存储方式为 LocalStorage 方式 #I2AKUA
    •   Furion 框架包描述文件,减少框架体积 #I2APAU
    •   App.CanBeScanTypesApp.EffectiveTypes #I2B0ZR
    •   App.ServiceProvider 属性并移除 App.GetDuplicateXXX 方法 #I2CYZE
    •   Db.GetDuplicateDbContextDb.GetNewDbContext #I2CZ04
    •   Db.GetSqlDispatchProxyDb.GetSqlProxy #I2DO9T
    •   Aop 服务拦截器,支持异步、同步两种方式 #I2B9HQ
    •   网络请求所有功能 #I2BMR7
  • 问题修复

    •   Swagger 规范化化结果不一致 bug #I2ACF3
    •   数据库新增或更新忽略空值操作方法报空异常 #I2AB6C
    •   Startup.cs Aop 全局拦截无效 #I2A7T2
    •   Token 过期后自动刷新 Token 无法获取最新的用户信息 bug #I2AWQI
    •   [ApiDescriptionSettings(Tag="xx")] 导致 swagger.json 报错 bug #I2B47R
    •   Mysql sql 数据库查询结果 tinyint 类型转换出错 bug #I2BEBM
    •   规范化结果多次包裹类型 bug #I2BHHZ
    •   动态 Api 基元类型数组问题 #I2BMS5
    •   sql 查询枚举类型转换异常 bug #I2BS2Y
    •   string.SqlQuerizeAsync<T1>() 拓展返回错误 bug #I2BSTS
    •   动态 Api 子类重写父类方法并取别名后 Swagger 异常 bug #I2C9VP
    •   网络请求 application/json 序列化大小写问题 #I2CRJC
    •   多数据库定位器实体嵌套关联 bug #I2CVN0
    •   跨域响应头设置无效 bug #I2CW5T
    •   远程网络请求代理打印到 MiniProfiler bug #I2CZBC
    •   远程请求响应拦截器 bug #I2D4DG
    •   SqlSugar 框架 AsQueryable() 一直追加参数 #I2DH1D
    •   自动刷新 Token 空异常 bug #I2DO29
    •   生成 JWT Token 不传过期时间出现验证 401 bug #I2DO8L
    •   AppStartup 排序无效 #I2DVD2
    •   未启用多语言服务时友好异常和验证出现空异常 #I2ECUJ
    •   数据校验字母和数字组合无法匹配 bug #I2EF2Q
    •   数据校验手机或固话无效 bug #I2M5IZ
    •   Dapper 拓展解析 SqlConnection 异常 bug #I2M5P2
    •   开启多语言后,EF 迁移异常 bug #I2M7DT
    •   IEntityTypeBuilder 不支持多重继承 bug #I2PAOD
    •   JwtHandler 设置自动刷新后,匿名访问无法通过 bug #I2SDOX
    •   Dapper 拓展中 SqlServer 数据库获取连接对象类型 bug #PR159
  • 其他更改

    •   Sql 查询结果映射检查 [NotMapper] 特性机制 #I34XD0
    •   依赖注入时排除 IDynamicApiController 接口 #I2ECTG
    •   MD5 加密性能 #PR158
  • 文档变化

    •   文档首页 #I34XBR
    •   网络请求文档 #I2APGJ
    •   多语言文档 #I2DOCL
    •   文档全文搜索引擎 #I34XAW
    •   全局静态类类型 #I34XB4
    •   框架可配置选项文档 #I34XB9
    •   事件总线文档 #I34XBI
    •   数据加解密文档 #I34XC0
    •   贡献指南文档 #I34XC8
    •   HttpContext文件上传下载 博客文章 #I34XCB
    •   文档小调整,小优化
  • 问答答疑

    •   跨域设置无效 #I2ASNJ
    •   MVC 视图无效,原因是 .cshtml 文件没有设置为 内容 #I2AXUU
    •   Sql 操作可以实现事务吗?#I2B0NX
    •   IRepository 操作数据库会打开多次数据库连接 #I2BB7B
    •   如何进入自定义 AppAuthorizeHandler 断点 #I2BGXY
    •   SqlSugar 注入问题 #I2C2AQ
    •   建议增加 API 签名验证,时效验证 #I2C6ET
    •   多数据库多租户同时使用 Add-Migration 报错 #I2CEHS
    •   ISqlSugarRepository 没有 Getxxx 方法 #I2CJLZ
    •   cli.ps1 如何将 sql 里的表导出成 model#I2CSUL
    •   手动修改 Swagger 终结点路径无效 #I2D608
    •   DefaultDbContext 不能识别 #I2DCZX
    •   各分层项目 Startup.cs 支持 Configuration #I2DDUP
    •   Aop 无法拦截,无效 #I2DEY8
    •   mysql 执行 Add-Migration 报错 #I2DSB8\
    •   Entity 创建时间和是否删除添加默认值 #I2E04H
    •   swagger 中多个 servers 设置 #I2E0IF
    •   全局筛选器 没有执行 #I2E5R4
    •   多数据库定位器疑问 #I2E77T
    •   cli.ps 逆向工程 Mysql 数据库报错 #I2E7I5
    •   Swagger 开发环境 applicationsettings.json 中文乱码 #I2EAG1
    •   增加指定路径程序集映射 #I2EEO2
    •   动态编译 cs 脚本文件 #I2EH66
    •   自定义中间件,返回的错误没有规范化结果 #I2NV8S
    •   Swagger 循环引用设置生成文档层级无效 #I2PLQQ
    •   配置文件支持 yaml 文件吗? #I2TJ3N
    •   修改数据库未 mysql 执行 Add-Migration 报错 #I2VR64
    •   多数据库使用定位器时报错 #I2VR8F
    •   Migration To Oracle 异常 #I2WBYQ
    •   开发时显示 Swagger,上线时关闭 Swagger,这需要怎么配置 #I2WOYV
    •   兼容 Mvc 复杂验证没有试验成功 #I2X3GV
    •   Aop 能不能支持无接口的类 #I2X8AS
    •   关于 JWT Token 自动刷新问题 #I2YD4K
    •   能否增加一个拓展的 Entity,增加一些拓展的属性 #I2YDKT
    •   Furion 无法还原包,使用NuGet 下载和通过最新的的脚手架下载都提示这个问题 #I30446
    •   复杂校验与特性验证不能并行 #I3046U

v1.7.0(已发布)

  • 新特性

    •   Furion.Extras.ObjectMapper.Mapster 拓展包 #I29LSJ
    •   Furion.Extras.Logging.Serilog 拓展包 #I2AAN8
    •   Furion.Extras.Web.HttpContext 拓展包 #I29LSM
    •   内置 Token 刷新机制支持 #I29K57
    •   动态数据库上下文,支持运行时执行 OnModelCreating #I28UDT
    •   支持依赖注入排除指定接口 #I29693
    •   规范化结果返回时间戳字段 #I29697
    •   基础 CURD 父类操作例子 #I296SR
    •   sql.Change("定位器完整类型名称") 支持 #I29LAB
    •   UpdateIncludeUpdateExclude 忽略空参数支持 #I29VUG
    •   数据库上下文内置假删除查询过滤器支持 #I29Y2R
    •   忽略空值排除默认时间格式 #I29VUV
    •   MiniProfiler 组件 #I297R9
  • 突破性变化

    •   AppAuthorizeHandler 授权管道为异步处理 #I29MD9
    •   Swagger 默认启用 JWT 授权支持 #I29LI4
    •   HttpContextUtilities 名称改为 HttpContextLocal #I29KQE
    •   UnifyResultContext 名称改为 UnifyContext #I29LLZ
    •   只有执行迁移命令才扫描种子数据 #I29E6P
    •   规范化结果 Successed 属性名为 Succeeded #I29NMV
    •   Mapster 对象组件,采用提供拓展方式 #I29D2M
    •   CacheManager 拓展类 #I29LU1
    •   SaveChanges 拦截器 #I292LO
  • 问题修复

    •   未注册的数据库上下文也被引用全局查询拦截器 bug #I29ZXJ
    •   手动返回 BadObjectResultValidationProblemDetails 结果类型时规范化结果失效 bug #I29ZU9
    •   动态 WebApi KeepNameKeepVerbSplitCamelCase 无效 bug #I29X90
    •   Sql代理 返回 元组 类型出错 bug #I29SMV
    •   401,403 状态码规范化返回值属性变大写 bug #I29M8Y
    •   HttpContext 空异常 bug #I29LU4
    •   接口无返回值没有应用规范化结果 bug #I29GT7
    •   前端 Less 配置文件导致主机启动失败 bug #I29E7P
    •   执行 sql 结果转泛型后属性重复赋值 bug #I29BUO
    •   Swagger 关闭 MiniProfiler 之后 组中组 失效 #I29789
    •   未启用规范化结果时异常返回 System.Object 字符 #I2969A
    •   正数数据验证 0 也验证通过 bug #I2955T
    •   非泛型类集成泛型接口依赖注入 bug #I294YT
    •   Swagger 不支持 new 覆盖父类的 bug #I28Z1A
    •   JsonSerializerUtility 没有公开 bug #I28WMI
    •   SqlSugar 拓展查询泛型类型注册异常 bug #I28VMT
    •   Furion Tools 不支持生成不同命名空间的实体 bug #I2A175
    •   全局拦截器无效 bug #I2A7T2
    •   新增或更新忽略空值空异常 bug #I2AB6C
  • 其他更改

    •   Token 生成加密算法 #I29KIH
  • 文档变化

    •   日志文档 #I28Y9D
    •   数据库上下文、实体拦截器、配置、一分钟入门等等文档
  • 问答答疑

    •   Swagger 如何实现授权访问 #I294F2
    •   如何实现多个数据库多对多实体配置 #I29G6S
    •   动态 WebApi 支持文件上传吗 #I29R5E
    •   多个数据库上下文无法生成迁移代码 #I2A6II

v1.4.0(已发布)

  • 新特性

    •   Furion 支持二级虚拟目录部署功能 #I28B77
    •   Furion.Template.RazorWithWebApi 脚手架 #I28QGI
    •   Furion.Template.BlazorWithWebApi 脚手架 #I27Z3O
    •   EFCore 时态查询拓展 #I28AJ
    •   [AppDbContext(连接字符串,数据库类型)] 配置支持 #I28QTB
    •   DateTimeOffsetDateTime 拓展方法 #I27MQA
    •   ValidationTypes 验证正则表达式智能提示 #I2801V
    •   ValiationTypes.WordWithNumber 验证 #I2805
    •   获取客户端和服务端 IP 地址 #I28QV9
  • 突破性变化

    •   .NET 5.0 版本至 .NET 5.0.1 版本 #I28QU
    •   视图引擎功能,优化不规范命名和新增字符串模板编译 #I28G0S
    •   数据库实体查找算法,并优化性能 #I28QUQ
    •   应用启动初始化性能和数据库第一次自动配置 DbSet 性能
  • 问题修复

    •   多数据库上下文配置定位器后实体无法正确生成 bug #I2888L
    •   多租户数据库上下文实体生成 bug #I2891G
    •   对象验证失败提示消息没有应用 JSON 大小写配置 bug #I27UTX
    •   仓储 InsertUpdate 方法指定 ignoreNullValues 无效 bug #I27UN6
    •   Controller 派生类如果贴了 [Route] 特性后出现在 Swagger 中 bug #I27TN7
    •   SqlScalar 执行 sql 返回 Nullable 类型出现转换失败 bug #I27S2N
    •   [UnitOfWork] 特性异常 bug #I27MLM
    •   sql 静态执行方式和 sql 高级代理无法监听数据库连接状态 bug #I27M4F
    •   更换 Json 序列化库无效 bug,如替换为 Microsoft.AspNetCore.Mvc.NewtonsoftJson #I27M43
    •   Furion Tools 工具生成模型 bug #I27XI5
    •   软删除没有生效 bug #I2804I
    •   Furion Tools 识别带多个 \\ 的连接字符串识别 bug #I280TS#PR91
    •   Furion Tools 无法取消生成 bug #I2816M
    •   DateTimeOffset 转本地时间差 8 小时 bug #I28BA9
    •   启用 bundle js&css 压缩后启动异常 bug #I28KR
    •   ValidationTypes.Required 无效 bug #PR98
    •   规范化结果OnValidateFailed 参数名拼写错误 bug #PR93#PR92
    •   授权管道验证失败还显示结果 bug #PR89
  • 其他更改

    •   README.md 友情连接地址 #PR88
    •   模板脚手架源码,添加 EFCore Tools#PR87
    •   README.md NuGet 图标 #PR85
    •  List<T>DateTable #PR97
  • 文档变化

    •   视图引擎模板文档 #I27ZVA
    •   EFCore 时态查询文档 #I28AJ, DOC
    •   仓储文档书写纰漏 bug #PR90
    •   选项文档错误 bug #PR86
    •   实体数据监听器 文档书写错误 bug #PR83
    •   数据库上下文、多数据库、脚手架等文档
  • 问答答疑

    •   希望 api 返回的值自动将 null 转为 ''[] #I286IJ
    •   添加网关功能 #I27TP7,【已关闭】
    •   SqlQuery<T> 获取单条记录方法 #I28M1V
    •   希望可以提供集成 Serilog 例子 #I282J4
    •   如何通过特性配置唯一约束 #I2891L
    •   怎么读取 appsettings.json 数组 #I27WU
    •   IRepository<TEntity> 出现空异常 #I281IE
    •   规范化接口问题问题 #I28NMZ
    •   统一返回值模型中 OnResponseStatusCodes 未执行 #I28NNL

v1.2.0(已发布)

  • 新特性

    •   雪花算法 #I26OXG, #PR78
    •   [AppDbContext] 配置数据库提供器支持 #I27G3T
    •   实体表数据更改监听接口 IEntityDataChangedListener #I278DD, #I278LQ
    •   全局服务接口 AOP 拦截功能 #I278CP
    •   定位器仓储 IDbRepository<TDbContextLocator> #I276Q3
    •   数据库操作 InsertOrUpdate 支持排除空字符串功能 #I272OG
    •   数据库操作 UpdateIncludeUpdateExclude 匿名对象支持 #I271X0
    •   数据验证传入空对象跳过验证支持 #I273R4
    •   应用启动时支持排除特定配置文件自动加载 #I26U0A
    •   单个实体表名前缀支持 #I26LX0
    •   MySql 数据库自动配置默认版本号 #I26XQ6
    •   授权处理程序代码
  • 突破性变化

    •   实体表数据更改监听接口 IEntityDataChangedListener #I278DD, #I278LQ
    •   全局服务接口 AOP 拦截功能 #I278CP
    •   雪花算法 #I26OXG, #PR78
  • 问题修复

    •   视图引擎加载外部程序集出错 bug
    •   依赖注入代理接口报空对象异常 bug
    •   EFCore 取消附加实体出错 bug
    •   数据库仓储在非 Web 请求下出现空异常 bug
    •   多个授权策略共存问题出现无效 bug
    •   友好异常 Oop.Oh 不支持普通方法 bug
    •   获取多租户对象时数据库上下文出现作用域验证失败 bug
    •   工作单元不支持 Sql代理 拦截 bug #I27GST
  • 文档变化

    •   实体数据监听器 文档
    •   一分钟入门、应用启动、官方脚手架、数据库操作指南、对象映射、规范化文档、异常处理、鉴权授权文档

v1.1.0(已发布)

  • 新特性

    •   Db.GetDbContext() 获取默认数据库上下文方法
    •   HttpContextUtility.GetCurrentHttpContext() 获取全局 HttpContext 上下文
    •   App.GetRequiredService<> 解析服务方法
    •   object.GetService<> 对象拓展方法
    •   策略授权 PolicyPipeline 基类方法,支持多重判断授权
    •   JWTEncryption.ValidateJwtBearerToken 手动验证静态方法
    •   全局数据库上下文 InsertOrUpdateIgnoreNullValuesEnabledEntityStateTracked 全局配置
    •   Swagger Jwt授权 全局授权参数 #I26GLR
    •   InsertOrUpdate 支持自定义判断条件功能 #I269Q1
    •   字符串字段小写命名支持 #I2695D
    •   字符串文本对比功能 #I268LE
    •   全局异常特性消息功能 #I2662O
    •   InsertUpdate 数据库忽略空值功能 #I264Q4
  • 突破性变化

    •   Fur 项目名为 Furion
    •   Db.GetRequestDbContext<>() 命名为 Db.GetDbContext<>()
    •   Db.GetDbContext<>() 命名为 Db.GetDuplicateDbContext<>()
    •   App.GetService<> 解析服务的底层逻辑,大大提高了解析服务的性能
    •   授权核心代码,保持和微软一致的授权规范 #I26DCB
    •   App.GetRequestService<> 方法
    •   ValidateJwtBearer Jwt 授权方法,无需手动判断了
  • 问题修复

    •   Furion 官方脚手架生成后编译异常 bug
    •   Tenant 内置属性不是 virtual 修饰 bug
    •   dockerfile 新命名构建失败 bug
    •   自定义角色授权和多个授权共存出现 403 bug #I26H1L
    •   httpContext.GetEndpoint() 空异常 bug #PR73
    •   Oops.Oh 空异常和不支持服务抛异常 bug #I26EFU#I26GM4
    •   cli.ps 生成文件编码乱码 bug #I26DVT
    •   Swagger 文件上传按钮不显示 #I26B6U
    •   规范化结果授权状态码序列化大小写不一致问题 #I26B26
    •   未启用规范化结果时中文乱码 bug #I268T5
    •   MySql 异步异常捕获不到 bug #I265SO
    •   cli.ps1 提示找不到数据库连接字符串 bug #I2647U
  • 其他更改

    •   代码性能小优化和小调整
  • 文档变化

    •   一分钟入门、安全鉴权、数据库等文档

v1.0.3(已发布)

  • 新特性

    •   Mvc 模板脚手架:Fur.Template.Mvc
    •   WebApi 模板:Fur.Template.Api
    •   Mvc/WebApi 模板:Fur.Template.App
    •   Razar Pages 模板:Fur.Template.Razor
    •   Blazor 模板:Fur.Template.Blazor
  • 突破性变化

    •   PagedListSystem.Collections.Generic 命名空间下
    •   解析服务性能问题,底层代码大量优化
  • 问题修复

    •   ApiSears.ControllerEnd不起作用 bug #I25KH6
    •   RemoteRequest 请求完成结果序列化属性大小写问题 #I25I8R
    •   HttpContext.GetEndpoinet() 空异常 bug #PR73
  • 文档变化

    •   入门文档、数据库上下文文档、多数据库操作文档

v1.0.2(已发布)

  • 新特性

    •   Pomelo.EntityFrameworkCore.MySql 最新 .NET 5 包配置 #I24ZQK
    •   .AddDateTimeJsonConverter(format) 时间格式序列化配置
    •   DateTimeDateTimeOffset 类型序列化格式配置 #I253FI
  • 突破性变化

    •   Mapster 包至 7.0.0
    •   App.Services 名为 App.ServiceProvider
    •   App.ApplicationServicesApp.GetRequestService<>()
    •   非 Web 主机注入拓展
  • 问题修复

    •   services.AddFriendlyException() 缺少配置注入 bug
    •   数据库上下文池被释放和高并发下内存溢出 bug #I2524K#I24UMN
    •   Sql代理 返回空数据时异常 bug #I24TCK
    •   工作单元 [UnitOfWork] 多数据库被释放 bug #I24Q6W
  • 其他更改

    •   EntityBaseEntity 所有属性为 vitural 修饰
    •   Jwt 读取和解析性能
    •   优化代码支持 C# 9.0 最新语法
    •   MD5 加密性能 #PR71
    •   无用或未使用代码
  • 文档

    •   数据库上下文、多数据库、一分钟入门文档

v1.0.0(已发布,.NET5)

  • 新特性

    •   网络请求 RemoteRequest 组件 #I1YYWD
    •   .AddInjectBase() 注入,只包含基础服务注入
    •   所有服务都支持 IServiceCollectionIMvcBuilder 注入
    •   抛异常状态码设置功能 StatusCode
    •   Swagger 序列化支持 Pascal 属性命名方式
  • 突破性变化

    •   所有的包为 .NET 5 正式版
  • 问题修复

    •   SqlProxy 代理异步处理 bug
    •   数据库类型 DatetimeDateTimeOffset bug
    •   属性首字母大小写序列化不匹配出现 null bug
    •   对象序列化中文出现乱码 bug
    •   默认序列化配置无效 bug
    •   数据库非依赖注入方式提交无效 bug
    •   应用程序池提交所有 DbContext 空异常 bug
    •   Saas 多租户 Tenant 类型字符串属性在 MySql 数据库下出现 longtext 类型 bug
    •   Mvc 自动验证字符串空值 bug #I24M2T
    •   枚举注释被覆盖 bug #I24N6J
    •   忽略规范化结果无效 bug #I24B8P
    •   Swagger 默认 ContentType 不是 applicaiton/json bug #I24F3U
    •   内置 System.Text.JsonNewtonsoft.Json 冲突 bug #I24F3U
  • 其他更改

    •   Fur 框架域名为:http://furion.baiqian.ltd
    •   仓储 FromSqlRawFromSqlInterpolated 接口位置
    •   数据加解密性能,#PR70
  • 文档

    •   README.md、框架介绍、数据库上下文、配置选项、多租户、跨域文档