Paradox 数据库原生 .NET 读取器






4.87/5 (29投票s)
在纯 .NET 代码中读取 Paradox 数据库的内容,无需使用 BDE 或其他库;包含索引支持。
引言
如果您正在开发访问 Paradox 数据库的软件,您可能正在使用 BDE (Borland Database Engine)。本文展示了如何使用 Paradox 数据库文件并直接从中读取一些数据。您还可以使用主索引来查找您需要的特定记录。如果 BDE 与您不合作,或者您不能或不想使用 BDE,这可能很有用。
背景
每个开发人员有时都必须处理某种黑盒 - 一种未公开的技术,只能通过某些外部接口访问,但存在一个问题 - 这个黑盒对我们来说非常神秘 - 有时它有效,有时它无效,无论你做什么,它似乎都有自己的情绪。然后我总是希望它能被打开,这样我就可以调试它,跟踪它,看看它的内部结构。您也是一个在快速链接之间有 Reflector 的开发人员吗?那么您可能会理解我为什么开始搜索一些 .NET 原生 Paradox 数据库读取器。我找不到任何一个,但我找到了一些 Paradox 格式规范,所以我决定自己编写一个并公开它,供其他人使用。非常感谢 Randy Beck 和 Kevin Mitchell 分享了 Paradox 内部结构,并且由于他们的材料是在线的,我将把精力集中在描述我自己的代码上。
Paradox 使用数据库对象将数据拆分到文件中 - 每个表都有自己的文件,每个索引也是如此。数据文件和索引具有非常相似的结构,因此我们可以在一个类中同时处理它们 - ParadoxFile
。BLOB 值也存储在它们自己的文件中,但我还没有实现该结构。
ParadoxFile
- 处理 Paradox 数据文件和索引中通用结构的基础类ParadoxFile.DataBlock
- 表示一个数据块ParadoxFile.FieldInfo
- 字段的数据类型ParadoxFile.V4Hdr
- 仅存在于某些 Paradox 文件/版本中的结构ParadoxTable
- 表示表数据文件ParadoxPrimaryKey
- 表示表索引ParadoxFileType
- 包含所有文件类型的枚举ParadoxFieldType
- 包含所有数据类型的枚举ParadoxRecord
- 表示一条数据记录ParadoxDataReader
- 标准IDataReader
实现
实际上,索引文件只是一个包含数据块索引和相关索引值的表。您可以直接读取它,但我也创建了一个机制,可以为您浏览索引树并选择所需的记录。您只需指定您想要应用于数据的条件。
ParadoxCondition
- 可用于在索引数据中搜索的条件的基础类ParadoxCondition
嵌套类 - 各种条件实现ParadoxCompareOperator
- 包含支持的比较运算符(==, !=, <, <=, >, >=)的枚举
使用代码
一种方法是直接打开一个表并开始枚举。这是通过以下代码段完成的。我们将创建一个 ParadoxTable
对象并使用 Enumerate
方法遍历记录。数据根据需要同步检索,因此如果我们在读取几个记录后停止读取,则不会执行冗余读取操作。
var table = new ParadoxTable(dbPath, "zakazky");
var recIndex = 1;
foreach (var rec in table.Enumerate())
{
Console.WriteLine("Record #{0}", recIndex++);
for (int i=0; i<table.FieldCount; i++)
{
Console.WriteLine(" {0} = {1}", table.FieldNames[i], rec.DataValues[i]);
}
if (recIndex > 10) break;
}
当然,在某些情况下,这种简单的方法是不够的。有时我们还必须读取大型数据库中间的某些数据,因此我们需要使用索引来最大限度地减少磁盘操作。在下一个示例中,我们将使用主键索引来查找键值在 1750 和 1760 之间的记录。首先,必须打开索引文件,然后我们将创建一个由两个比较操作组成的条件。我们可以通过调用索引上的 Enumerate
方法来浏览数据。在这种情况下,我们将使用具有 IDatareader
实现的 ParadoxDataReader
类,因此如果之前使用过 BDE,则无需重写大量代码。
var index = new ParadoxPrimaryKey(table, Path.Combine(dbPath, "zakazky.PX"));
var condition =
new ParadoxCondition.LogicalAnd(
new ParadoxCondition.Compare(
ParadoxCompareOperator.GreaterOrEqual, 1750, 0, 0),
new ParadoxCondition.Compare(
ParadoxCompareOperator.LessOrEqual, 1760, 0, 0));
var qry = index.Enumerate(condition);
var rdr = new ParadoxDataReader(table, qry);
recIndex = 1;
while (rdr.Read())
{
Console.WriteLine("Record #{0}", recIndex++);
for (int i = 0; i < rdr.FieldCount; i++)
{
Console.WriteLine(" {0} = {1}", rdr.GetName(i), rdr[i]);
}
}
限制
- 只读解决方案
- 并非所有数据类型都受支持
- 没有 SQL、LINQ 或其他复杂查询
- 在写入期间,您不应该使用活动的数据库
历史
- 1.0 - 初始版本
- 1.1 - 更新了
Number
数据类型支持(感谢 Tonki) - 1.2 - 附加数据类型支持(感谢 Mark Kuin、Christopher Erker 和 Tonki 再次感谢)