我要问的第一个问题是:我真的需要这样做吗?类型化的 DataSet 设计器已经为您提供了一个用于定义存储过程和 DataTable 之间的映射的工具。如果您仔细设计您的 DataSet,那么您已经为每个 DataTable 提供了一个 Fill 方法。重新发明那个轮子有意义吗?
我认为可能。有一种方法可以维护该映射真的很酷,但是该映射中的所有内容在编译时都被冻结了。如果要更改映射,则需要重建程序集。此外,类型化的 DataSet 设计不处理返回多个结果集的存储过程。如果要一般映射参数和值,则必须使用反射从 Fill 方法中获取参数列表。如果您考虑这些因素(以及我没有想到的其他因素),可能会发现使用现有工具不是要走的路。
在这种情况下,在我看来,您的目标是能够从一系列存储过程中使用尽可能少地了解实现细节的代码填充 DataSet。所以这是一个将由元数据驱动的过程。当您拥有由元数据驱动的流程时,从长远来看,对您而言最重要的是维护流程使用的元数据的难易程度。一旦你让代码工作,你可能不会接触太多。但是你会不断地调整元数据。
如果我从这个角度来看问题,我认为要做的第一件事就是设计一个类型化的 DataSet 来包含元数据。这给了我们很多东西,否则我们必须弄清楚:
- 持久性格式
- 构建绑定 UI 的直接途径
- 如果我们决定走这条路,那么将元数据保存在数据库中的同样简单的方法
- 用于导航数据的对象模型。
在此 DataSet 中,您将有一个 DataSetType 表,以您打算能够填充的每个类型化 DataSet 的类型为键。它将有一个子 StoredProcedures 表,每个被调用的 SP 都有一行。那将有两个子表,Parameter 和 DataTableType。对于 SP 预期返回的每个结果集,将有一个 DataTableType 行,按序号位置排序。DataTableType 表将有一个子 ColumnMapping 表。您将在该表中维护结果集中的列与您正在填充的表中的列之间的映射。
确保您的所有 DataRelations 都是嵌套的,并且您已经为关系指定了合理的名称。(我喜欢FK_childtablename_parenttablename
。)
一旦你有了这个,类的设计就变得非常简单了。该类具有对元数据 DataSet、Connection 等的引用,并且它公开了一个具有此签名的方法:
public void FillDataSet(DataSet targetDs, Dictionary<string, Dictionary<string, KeyValuePair<string, string>> parameterMap);
您首先使用 targetDs 的 Type 来查找顶级 DataSetType 行。然后所有私有方法遍历 DataTable.GetChildRows() 返回的 DataRows 列表。并且您在类设计中添加一个或两个事件,以便在它执行操作时可以引发事件,让调用应用程序知道它是如何进行的。
可能我希望重构此设计的第一个地方是让我对填充过程进行更细粒度的控制。例如,按照设计,每个类型化的 DataSet 只有一组 SP。如果我只想填充 DataSet 的一个子集怎么办?按照设计,我不能。但是您可以轻松地将 DataSetType 表的主键分为两部分,其中部分是 DataSet 类型和一些字符串键(名称如 SPSetName 或 OperationName),然后将键的第二部分添加到 FillDataSet 参数列表.