XYS.FR 4.2.0

dotnet add package XYS.FR --version 4.2.0
                    
NuGet\Install-Package XYS.FR -Version 4.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="XYS.FR" Version="4.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="XYS.FR" Version="4.2.0" />
                    
Directory.Packages.props
<PackageReference Include="XYS.FR" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add XYS.FR --version 4.2.0
                    
#r "nuget: XYS.FR, 4.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package XYS.FR@4.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=XYS.FR&version=4.2.0
                    
Install as a Cake Addin
#tool nuget:?package=XYS.FR&version=4.2.0
                    
Install as a Cake Tool

XYS.FR.V3

FastReport 数据契约与配置层。只负责:把 POCO 实体注册为 DataSet 表结构、生成 FastReport Designer 所需的 FRTables.frd、加载模板/行列对照配置。不含任何渲染引擎(PDF/图片/打印由 XYS.FRNet / XYS.FRCore 等宿主项目实现,它们继承本项目的 BaseFRService)。

  • 目标框架:net8.0 / net462 / netcoreapp3.1 / netstandard2.0 / netstandard2.1
  • 依赖:XYS.Utils.SysXYS.Utils.Log4Net(同解决方案项目引用)
  • 程序集名:XYS.FR(命名空间同名)

核心概念

类型 职责
IFREntity / FRBaseEntity 实体标记接口 / 抽象基类;子类用 [FRExport] 标注要导出的属性
FRExportAttribute 属性级导出标记,可选 Name 覆盖列名、IsExport=false 显式排除
DataStruct 静态注册中心:Register(Type) 幂等注册 → GetSet() 拿到空 DataSet 副本 → EnsureXmlFile() 落盘 Designer 用的 frd
ExportData.ExportEntity 把实体填充到 DataSet 里的 DataRow
FRReport 一次打印/导出的总载荷:模板号 + 实体列表 + 份数/副本名
IConfigManager / XmlConfigManager 模板号 → 模板文件名、行 ID → 列名对照
IFRExportService 导出契约:ExportStream。所有渲染宿主都实现
IFRPrintService 打印契约:Print仅由具备打印能力的引擎实现,与导出刻意分开
BaseFRService 服务基类,实现 IFRExportService;模板方法:GetFRTypes + InitFRConfig;调用 Init() 一次完成注册 + 配置

快速使用

1. 定义实体

using XYS.FR;

public class LabItem : FRBaseEntity
{
    [FRExport] public string ItemName { get; set; }
    [FRExport] public string Value { get; set; }
    [FRExport] public string Unit { get; set; }

    [FRExport(false)] public string InternalNote { get; set; }   // 不导出
    [FRExport(true, "结果标志")] public string Flag { get; set; } // 列名重命名
}

2. 继承 BaseFRService

public sealed class LabFRService : BaseFRService
{
    public LabFRService()
    {
        LoggerName = "LabFRService";
        ActualType = typeof(LabFRService);
        Init();  // 注册实体 + 加载配置
    }

    protected override List<Type> GetFRTypes() => new List<Type>
    {
        typeof(FRInfo),
        typeof(LabItem),
    };

    protected override void InitFRConfig()
    {
        var cfg = new XmlConfigManager();  // 默认 AppBase/Config/fr.config.xml
        cfg.Init();                        // 失败会抛异常,视为致命错误
        ConfigManager = cfg;
    }

    public override void ExportStream(FRReport report, Stream stream, ExportType type = ExportType.Pdf)
    {
        // 由具体渲染引擎实现(见 XYS.FRNet / XYS.FRCore)
    }
}

3. 生产 FRReport 并渲染

var report = new FRReport { ModelNo = "LAB001", RID = "R123" };
report.Items.Add(new FRInfo { ID = "1", RID = "R123", PatName = "张三" });
report.Items.Add(new LabItem { ID = "1", RID = "R123", ItemName = "WBC", Value = "6.5" });

using (var fs = File.Create("out.pdf"))
{
    labSvc.ExportStream(report, fs, ExportType.Pdf);
}

4. 配置文件 Config/fr.config.xml 示例

<?xml version="1.0" encoding="utf-8"?>
<frConfig>
  <modelMap>
    <model modelNo="LAB001" name="常规生化" alias="lab" fileName="lab_normal.frx" />
    <model modelNo="LAB002" name="血常规"   alias="cbc" fileName="lab_cbc.frx" />
  </modelMap>
  <mapMap>
    <map tType="0" rowId="WBC" colName="C0" />
    <map tType="0" rowId="RBC" colName="C1" />
  </mapMap>
</frConfig>

关键契约

成员 语义
DataStruct.Register(Type) 幂等注册;非 IFREntity 或已注册类型静默跳过
DataStruct.Register(IEnumerable<Type>) 批量注册
DataStruct.GetSet() 返回空数据集副本(含全部注册表结构)
DataStruct.EnsureXmlFile() 磁盘无 FR/FRTables.frd 时按当前注册生成;已存在不覆盖
DataStruct.Types 已注册类型只读快照
ExportData.ExportEntity(entity, ds) 单实体填充;表未注册抛 InvalidOperationException
BaseFRService.Init() 注册 + 生成 frd + 加载配置;同一进程多子类并存安全
BaseFRService 的打印 不提供。需要打印的宿主自行声明实现 IFRPrintService
BaseFRService.OpenModel(modelNo) 打开模板内容流,转发到 ConfigManager.OpenModel;渲染宿主只认这个口子
BaseFRService.ModelRoot 转发属性,实际存放于 BaseConfigManager.ModelRoot
IFRExportService.ExportStreamAsync 默认实现 = 线程池 + 丢弃结果式取消;宿主可重写接引擎的真取消
IConfigManager.OpenModel(modelNo) 调用方负责释放返回的流;默认实现读本地文件
BaseConfigManager.ModelRoot 模板根目录,未设置时惰性取 AppBase/FR
XmlConfigManager(string) 可通过构造函数注入配置文件路径
IConfigManager.Maps / Models IReadOnlyList,实现类内部维护

4.2.0 变更(相对 4.1.0)

破坏性(两个接口都加了成员,外部若有直接实现者需同步改;netstandard2.0 不支持默认接口方法,无法用 DIM 兜底)

  • IConfigManager 新增 Stream OpenModel(string modelNo)
  • IFRExportService 新增 Task ExportStreamAsync(FRReport, Stream, ExportType, CancellationToken)

非破坏性

  • ModelRootBaseFRService 下沉到 BaseConfigManagerBaseFRService.ModelRoot 保留为转发属性,既有子类的 ModelRoot = ... 写法照旧有效(含"在 Init() 之前赋值"这种推荐写法:值会先暂存,由 Init() 补写给配置管理器)
  • BaseFRService 新增 protected virtual Stream OpenModel(string modelNo)
  • BaseConfigManager 新增 GetModelFullNameOpenModel 默认实现(读本地文件,FileShare.Read 共享打开,因为服务端会并发渲染同一份模板)

模板可以不在文件系统上了

改动前渲染宿主只能拿到一个磁盘路径(GetModelFullName);改动后统一走 OpenModel 取内容流。 这样模板才可能入对象存储并带版本号——服务端渲报告、客户端渲标签、设计器改模板,三方拿到同一份且可回滚。 模板留在各机文件系统上,必然每台一份副本、改了不同步、改坏了退不回去。

宿主要从 MinIO 取模板,只需自己实现 IConfigManager(或继承 BaseConfigManager)并重写 OpenModel, 渲染宿主一行不改:

public sealed class MinioConfigManager : BaseConfigManager
{
    public override void Init() { /* 加载 models / maps */ }

    public override Stream OpenModel(string modelNo)
    {
        //GetModelName 未匹配时返回 default.frx,与本地实现语义一致
        return _minio.GetObject(_bucket, GetModelName(modelNo));   //调用方负责释放
    }
}

GetModelFullName 仍然保留且行为不变,但渲染宿主已不再使用它——日志里的模板标识也随之从 "模板全路径"改成"模板编号(文件名)",因为模板可能根本没有路径。

为什么要异步 + 取消

报告预览界面是"左侧列表选行 → 右侧显示渲染后的 PDF"。用户按方向键快速划过 20 行, 会产生 20 个渲染请求,其中 19 个在返回时已经没人要了。更要紧的不是浪费,是乱序响应: 第 3 行的响应晚于第 7 行返回时,右侧显示的是第 3 行的报告,而左侧高亮在第 7 行—— 审核医生照着右边看,可能给别人的报告点通过。这是临床安全缺陷,一个人手快就能触发。

取消不是唯一的防线(调用方还要做响应认领和防抖),但同步接口下连取消这一层都没有。

BaseFRService 的默认实现是 Task.Run(..., ct)ct 能挡住尚未开始的渲染, 已经进入引擎的那一份仍会跑完、结果被丢弃。渲染引擎若提供可中断的准备阶段,宿主应重写接上去—— XYS.FRCore 在 net462 / net8.0 上就是这么做的(Report.PrepareAsync(ct))。

注:本次同样是破坏性变更而只升次版本号,理由与 4.1.0 一致(三个项目间均为 ProjectReference)。

4.1.0 变更(相对 4.0.0)

破坏性

  • IFRService 拆分为 IFRExportServiceExportStream)与 IFRPrintServicePrint),原 IFRService 已删除
  • BaseFRService 不再声明 Print 抽象成员,只实现 IFRExportService

为什么

原来 IFRService 把导出与打印绑在一起,导致每个渲染宿主都被迫实现两者。但 FastReport.Core 是跨平台版,不含任何打印 APIReport.Print / Report.PrintPrepared / PrintSettings 在 2023.3.13 中均不存在),XYS.FRCore 只能写出一个恒抛 NotSupportedExceptionPrint——典型的 Liskov 违例。拆分后:

宿主 实现
XYS.FRNet(FastReport .NET 完整版) BaseFRService + IFRPrintService
XYS.FRCore(FastReport.Core 跨平台版) BaseFRService,不实现打印

XYS.FRCore 中那个恒抛异常的 Print 已随之整块删除。

注:本次为破坏性变更,按 semver 本应升主版本。因 4.0.0 当天才提交且从未对外发布(三个项目均为 ProjectReference),经决策只升次版本号 4.1.0,特此记录。

4.0.0 变更(相对 3.1.0)

破坏性

  • ExportType 枚举值改 PascalCase:pdf/jpg/html/xlsPdf/Jpg/Html/Xls
  • XML DTO 类重命名:FRModel_XmlFRConfigXmlFRModel_MFRModelXmlFRMap_MFRMapXml(XML 节点名不变,兼容旧配置文件)
  • IConfigManager.Maps / ModelsList<T> 收紧为 IReadOnlyList<T>;外部不再可整体替换
  • IFREntity 接口去掉误导性的 [FRExport] 装饰(C# 属性 Attribute 不从接口继承,原来就没生效)
  • 删除死接口 IFRModel、空壳 ISetting/BaseSettings
  • FRInfo 拼写修正:Equipentment→EquipmentSmapleExp→SampleExpColletTime→CollectTimeInceptTime→AcceptTimeInceptSign→AcceptSign
  • DataStruct 从"通过公共 TList.Add 修改集合"改为"通过 Register() 幂等注册";InitXMLStruct/InitRamStruct 已由 Register + EnsureXmlFile 取代
  • BaseConfigManager.Maps/Models 公开的可写属性已改为只读;子类改用 protected 字段 _maps / _models

修复

  • _initFlag 是静态导致多子类竞态:现改为 HashSet<Type> per-type 幂等注册,多个 BaseFRService 子类可安全共存
  • [FRExport(false)] 曾被忽略DataStruct.IsExport(PropertyInfo) 现真正读取 IsExport 属性
  • FRExportAttribute.Name 曾被忽略:现作为导出列名生效
  • XmlConfigManager.Init 静默吞异常:文件不存在 / 解析失败 / 反序列化 null 都会抛异常向上传播
  • Models/Maps 节点为 null 时 NRE:分别判空
  • throw ex; 丢失堆栈:移除无意义 try/catch
  • ExportData.IsExportDataStruct.IsExport 重复代码:统一到 DataStruct
  • ExportData 抛通用 Exception:改为 InvalidOperationException / ArgumentNullException

清理

  • 依赖由 PackageReference XYS.Utils.Sys 2.2.1.1 / XYS.Utils.Log4Net 2.1.1.1 改为 ProjectReference,与解决方案统一
  • DataStruct.Gender*AttrDicBuild*Attrs(原方法名拼写疑似 Generate 之误)
  • static readonly string 常量 → const
  • 删除 DataStruct.ConvertXml2DataTable/ConvertXml2TableRelation/GetRelationColumns 等死代码
  • MethodBase.GetCurrentMethod().Name 反射调用 → nameof

建议

  • Init() 在服务构造末尾调用WorkerCount 等属性设置在前
  • XmlConfigManager 配置加载失败视为致命错误,让 App 启动失败胜过运行期打空模板
  • [FRExport(name: "...")] 用于列名重命名,Designer 端字段名与 C# 属性名可解耦
  • 同进程内允许多个 BaseFRService 子类共存(如 Lab / Exam / EMR),共享同一 DataStruct 实体注册表
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 is compatible. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (10)

Showing the top 5 NuGet packages that depend on XYS.FR:

Package Downloads
XYS.FR.Net462

Description

XYS.FR.ZhiFang

Package Description

XYS.FR.NetCore

Package Description

XYS.FRCore

FastReport.Core 渲染宿主:实现 XYS.FR 的 BaseFRService 导出契约,提供 PDF/JPG/HTML/XLSX 导出。不支持打印(Core 版无打印 API)。

XYS.FR.NetFr4

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.2.0 200 9/5/2026
4.0.0 114 8/17/2026
3.0.2.7 94 9/4/2026
3.0.2.6 340 4/18/2024
3.0.2.5 299 3/13/2024
3.0.2.4 282 1/16/2024
3.0.2.3 268 1/16/2024
3.0.2.2 268 1/16/2024
3.0.2.1 265 12/4/2023
3.0.1.11 267 11/26/2023
3.0.1.10 240 11/26/2023
3.0.1.9 260 11/25/2023
3.0.1.8 235 11/25/2023
3.0.1.7 247 11/24/2023
3.0.1.6 266 10/27/2023
3.0.1.5 239 10/27/2023
3.0.1.4 239 10/26/2023
3.0.1.3 1,016 9/14/2022
3.0.1.2 893 9/13/2022
2.0.3.3 836 9/14/2022
Loading failed