我在审阅InterSystems 员工编程挑战赛的参赛作品时,突然灵光一闪:何不一边解决这个挑战,一边完成7 月“文章奖励计划”中的一个主题呢? 最终成果是一个紧凑的端到端示例,演示了如何从 C# 调用 InterSystems IRIS,并展示了两个互补的 .NET API:用于高性能对象持久化的 XEP(eXtreme Event Persistence)以及用于查询和分析的 ADO.NET。
在本文中,我们将使用 InterSystems IRIS 和 C# 构建一个完整的数据处理管道。 我们将使用 XEP(eXtreme Event Persistence)API 将 Gaia 纪元光度测量数据导入 IRIS,自动创建持久化模式,通过 ADO.NET 使用 SQL 分析数据,并将结果导出回 CSV 文件。整个过程无需手动编写任何 ObjectScript 类。
最终,您将获得一个轻量级的 ETL 应用程序,该应用程序展示了多种 InterSystems 技术的协同工作:
- 用于高速对象持久化的 XEP
- ADO.NET 用于 SQL 分析
- InterSystems IRIS 兼具运营数据库和分析引擎双重功能
数据集
本示例使用了一个包含 Gaia 历元光度观测数据的 CSV 文件。每行代表一个由唯一 source_id 标识的天体。在我们的分析中,我们关注以下两列:
- bp_flux — 蓝光光度计通量测量值
- rp_flux — 红光光度计通量测量值
与普通的标量值不同,这些列包含随时间收集的测量值数组。
例如:
bp_flux = [7922.41,7754.46,NaN,8083.01,65199.26]
rp_flux = [6211.03,6120.82,6084.50,6075.42]现实中的科学数据集很少是完美的,因此数组中可能包含:
- 缺失值
- null
- NaN
- 无穷大
- 格式错误的值
该应用程序在计算统计数据时会自动忽略这些值。
应用程序架构
该应用程序遵循四个简单步骤:
- 读取 CSV 文件。
- 创建持久化 XEP 对象并将其导入 InterSystems IRIS。
- 使用 ADO.NET 执行 SQL 查询,以识别波动性较大的数据源。
- 将查询结果导出到一个新的 CSV 文件中。
整个工作流由一个 C# 控制台应用程序实现。
定义持久化对象
XEP 最出色的功能之一在于,您无需亲自编写 ObjectScript 持久化类。相反,您只需定义一个普通的 C# 类,XEP 便会自动在 IRIS 内部生成相应的持久化类。这种方法使开发人员能够完全使用 C# 进行开发,同时仍可充分利用 InterSystems IRIS 的持久化和 SQL 功能。
我们的类包含:
- source_id
- 最小 BP 通量
- 最大 BP 通量
- 最小 RP 通量
- 最大 RP 通量
在从输入数组中移除所有无效测量值后,会在对象构建过程中计算出最小值和最大值。
namespace Gaia
{
public sealed class EpochPhotometry
{
public long source_id;
// Stored calculated fields.
public double bp_min_flux;
public double bp_max_flux;
public double rp_min_flux;
public double rp_max_flux;
internal EpochPhotometry(
long sourceId,
double[] bpFlux,
double[] rpFlux)
{
source_id = sourceId;
bp_min_flux = GetMinimum(bpFlux);
bp_max_flux = GetMaximum(bpFlux);
rp_min_flux = GetMinimum(rpFlux);
rp_max_flux = GetMaximum(rpFlux);
}
private static double GetMinimum(double[] values)
{
if (values == null || values.Length == 0)
{
return 0;
}
double minimum = double.PositiveInfinity;
bool found = false;
foreach (double value in values)
{
if (!double.IsFinite(value))
{
continue;
}
if (value < minimum)
{
minimum = value;
found = true;
}
}
return found ? minimum : 0;
}
private static double GetMaximum(double[] values)
{
if (values == null || values.Length == 0)
{
return 0;
}
double maximum = double.NegativeInfinity;
bool found = false;
foreach (double value in values)
{
if (!double.IsFinite(value))
{
continue;
}
if (value > maximum)
{
maximum = value;
found = true;
}
}
return found ? maximum : 0;
}
}
}程序运行后,您可以看到自动生成的类:
Class Gaia.EpochPhotometry Extends %Library.PersistentXEP [ Final, GeneratedBy = %FOI.Schema, ProcedureBlock ]
{
Parameter IMPORTTIMESTAMP = "19/07/2026 00:32:43";
Parameter IMPORTFLAGS = 1;
Property "source_id" As %Library.BigInt;
Property "bp_min_flux" As %Library.Double;
Property "bp_max_flux" As %Library.Double;
Property "rp_min_flux" As %Library.Double;
Property "rp_max_flux" As %Library.Double;
Storage Default
{
<Data name="EpochPhotometryDefaultData">
<Value name="1">
<Value>source_id</Value>
</Value>
<Value name="2">
<Value>bp_min_flux</Value>
</Value>
<Value name="3">
<Value>bp_max_flux</Value>
</Value>
<Value name="4">
<Value>rp_min_flux</Value>
</Value>
<Value name="5">
<Value>rp_max_flux</Value>
</Value>
</Data>
<DataLocation>^E6RM.EAvH.1</DataLocation>
<DefaultData>EpochPhotometryDefaultData</DefaultData>
<ExtentLocation>^E6RM.EAvH</ExtentLocation>
<IdLocation>^E6RM.EAvH.1</IdLocation>
<Index name="IDKEY">
<Location>^E6RM.EAvH.1</Location>
</Index>
<IndexLocation>^E6RM.EAvH.I</IndexLocation>
<StreamLocation>^E6RM.EAvH.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
} 读取 CSV 文件
该应用程序使用 CsvHelper 库读取 Gaia 数据集。每行数据都会被转换为一个 EpochPhotometry 对象。在解析过程中,程序会:
- 验证必填列
- 解析数值数组
- 忽略无效值
- 计算最小和最大通量值
如果某个测量值无效,程序不会因此丢弃整个观测数据,而是仅跳过该有问题的数组元素。其余有效值将继续参与计算。
使用 XEP 导入数据
对象创建完成后,应用程序将通过 XEP 连接到 InterSystems IRIS。导入过程仅包含以下几个步骤:
- 连接到 IRIS
- 导入模式(如有必要)
- 清除之前的数据集
- 分批存储对象
persister.ImportSchema(XepClassName);
irisEvent = persister.GetEvent(XepClassName);
irisEvent.Store(batch);该应用程序以每批 5,000 个对象为单位存储记录,这大大降低了逐条插入记录的开销。
此时,数据已完全持久化,并可通过 SQL 立即访问。
使用 ADO.NET 进行查询
应用程序的第二部分演示了标准的InterSystems IRIS ADO.NET提供程序。我们不再直接遍历对象,而是使用SQL进行查询。对于每个数据源,我们计算:
percentage_change_bp = ((bp_max_flux - bp_min_flux) / bp_min_flux) * 100
percentage_change_rp = ((rp_max_flux - rp_min_flux) / rp_min_flux) * 100随后,我们确定哪个百分比更大,并仅保留最大变化率超过100%的来源。由于计算是在SQL内部进行的,因此C#中无需进行额外处理。
查询返回:
- 源标识符
- 最小BP通量
- 最大BP通量
- 最小 RP 通量
- 最大 RP 通量
- BP百分比变化
- RP百分比变化
- 最大百分比变化
结果通过受管理的 ADO.NET 提供程序直接从 IRIS 流式传输,并写入一个新的 CSV 文件。
结果
生成的 CSV 文件仅包含根据我们的过滤标准表现出显著变异性的数据源。

运行应用程序
在运行项目之前,请安装:
- 可访问命名空间的 InterSystems IRIS
- 与 InterSystems .NET 程序集兼容的 .NET SDK
- CsvHelper
运行应用程序的机器必须能够访问 IRIS SuperServer 的端口。示例中的默认端口为 `1972`。
在项目目录下,运行:
dotnet add package CsvHelper
dotnet add package InterSystems.Data.IRISClient
dotnet add package InterSystems.Data.XEP示例项目引用:
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="InterSystems.Data.IRISClient" Version="2.7.0" />
<PackageReference Include="InterSystems.Data.XEP" Version="2.5.0" />
</ItemGroup>构建项目
dotnet build运行项目,并将输入和输出 CSV 文件的路径作为命令行参数传递。在 Windows 系统上,也可以使用绝对路径:
dotnet run -- "D:\GitHub\dc-csharp-example\data\in\EpochPhotometry.csv" "D:\GitHub\dc-csharp-example\data\out\EpochPhotometryChanges.csv"为什么选择 XEP?
尽管 InterSystems IRIS 支持多种持久化方案,但在应用程序已具备丰富的 .NET 对象模型时,XEP 尤为具有吸引力。它允许开发人员:
- 编写普通的 C# 类
- 自动生成持久化 IRIS 类
- 高效存储数千个对象
- 立即使用 SQL 查询数据
- 避免编写 ObjectScript 持久化代码
对于ETL作业、科学计算工作负载、遥测以及其他面向对象的数据管道,这使得XEP成为连接.NET应用程序与InterSystems IRIS之间的便捷桥梁。