|
[ZT]代码地震(作者:王咏刚 2004 年1 月)(5) 程序中的数据访问控制组件使用struct 结构来表示数据库 中每张表的结构。例如,对于表Transactions,数据库初始 化脚本中的创建语句为(以Transact-SQL 语言为例): create table [Transactions] ( [Name] Varchar(20), [Code] Varchar(20), [RefCode] Varchar(20), [RoleID] Int, [OpNodeID] Int, [ValidDate] Datetime ); 在C#语言中定义的,对应的数据结构为: struct Transaction { public string Name; public string Code; public string RefCode; public int RoleID; public int OpNodeID; public DateTime Datetime; } 为了正确地存取和维护内存中的Transaction 数据结 构,C#代码中还必须包含Transactions 表中所有字段名称、 限制条件以及相关查询语句或存储过程的定义。 这种编码方式在数据库应用系统的开发中十分常见, 它给我们带来的麻烦也非常明显:需要修改表结构时,程 序员必须首先改变每个数据库初始化脚本中的SQL 语句, 然后再依次修改数据访问控制组件中的struct 结构和与变 更字段相关的所有定义语句。 此外,“银证通”项目组在设计数据访问控制组件的接 口时还存在一个致命的问题——每个接口方法都将需要传 递的字段一字排开,像下面这样罗列在参数表里: public bool NewTrans(string Name, string Code, string RefCode, int RoleID, int OpNodeID, DateTime Datetime); 这样做的后果是,数据库结构改变时,所有接口方法 的参数列表都要调整,其他组件中所有调用这些接口方法 的语句也要相应地加以调整。 上面这两个问题都属于与数据库相关的代码重复问 题。解决后一个问题的办法相对简单,只要坚持在类似的 参数传递中只传递数据结构的指针或引用就可以了,像这 样: public bool NewTrans(Transaction data); 前一个问题,即,表结构的数据库表示(SQL 脚本) 与代码表示(C#中的struct 和相关定义)之间的重复问题, 就不那么好解决了。而且,在基于组件和分层的系统设计 中,系统的逻辑层次越多,类似的重复也就越多。其实, 对此类问题,即便是微软的工程师也很难给出既简单又有 效的解决方案。在Visual Studio .NET 提供的示例程序 Duwamish 中,数据库里几乎每一个表结构都会在数据库 创建脚本、存储过程代码、业务实体层代码、数据访问层 代码中反复出现。在Duwamish 中修改数据库的结构显然 也是一件苦差事,因为任何一项数据结构上的改动都必然 引发上述四类源代码的修改操作。 我所见过的一种相对简单的解决方案是,将数据库的 生成、修改、查询等SQL 语句作为字符串常量嵌入到代码 中,与struct 结构以及相关的定义放在一起。系统提供的 数据库安装程序也使用C#语言实现,并直接利用代码中保 存的SQL 指令创建表结构。如果发生表结构的变更,程序 员只要在一个源文件里,集中修改所有与该表相关的代码 就可以了。 第二种可行的方法是使用.NET 平台提供的RTTI 和 Reflection 机制,直接从C#代码中的数据结构(在运行时 获取struct 结构中每个元素的名称和数据类型)组合出与 该表的创建、修改和查询相关的SQL 语句。这种方法对编 程语言和编程技术的要求较高,而且只适用于提供了完善 的RTTI 和Reflection 机制的语言(如C#、Java 等语言)。
|