冬Blog

醉心技术、醉心生活
  博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

微型项目实践(2):用测试驱动代码生成

Posted on 2008-05-03 16:20  冬冬  阅读(4260)  评论(12编辑  收藏  举报

上一篇文章中,我们分析了系统的两个实体类,并且给出了对应的实体XML。今天我们来看看如何根据这个XML进行代码生成。

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Entities xmlns="http://it.ouc.edu.cn/EntityDescription/V2">
   3:    <Entity title="日志" name="Blog" module="Blogs">
   4:      <Item title="标题" name="Title" type="text" require="true"/>
   5:      <Item title="内容" name="Content" type="longtext" require="false"/>
   6:      <Item title="所属分类" name="BlogClass" type="entity" entityName="BlogClass" require="false"/>
   7:      <Item title="创建时间" name="CreateDateTime" type="datetime" require="true"/>
   8:      <Item title="更新时间" name="UpdateDateTime" type="datetime" require="true"/>
   9:    </Entity>
  10:    <Entity title="日志分类" name="BlogClass" module="Blogs">
  11:      <Item title="名称" name="Name" type="text" require="true"/>
  12:      <Item title="描述" name="Description" type="text" require="false"/>
  13:    </Entity>
  14:  </Entities>
 

这里我们主要是生成两种最重要的代码,一是数据库的建库脚本,二是实体的CS类。
我们使用测试来驱动代码生成,在项目DongBlog.Test中添加一个新的单元测试:Util.cs。对应的代码如下:

   1:  using ...
  12:   
  13:  namespace DongBlog.Test
  14:  {
  15:      /// <summary>
  16:      /// 数据库相关的工具方法
  17:      /// </summary>
  18:      [TestClass]
  19:      public class DatabaseUtil
  20:      {
  21:          /// <summary>
  22:          /// 构造建库脚本
  23:          /// </summary>
  24:          [TestMethod, Description("构造建库脚本")]
  25:          public void Util_CreateDatabaseScript()
  26:          {
  27:              var entities = getEntities();
  28:              string sqlText = MsSqlServerScriptBuilder.getSqlScript(entities);
  29:   
  30:              Debug.WriteLine("");
  31:              Debug.WriteLine("The following is the database creating script:");
  32:              Debug.WriteLine("=============================彪悍的分割线==============================================================================");
  33:              Debug.WriteLine("");
  34:              Debug.WriteLine("");
  35:              Debug.WriteLine(sqlText);
  36:              Debug.WriteLine("");
  37:              Debug.WriteLine("");
  38:              Debug.WriteLine("=============================又见彪悍的分割线==========================================================================");
  39:          }
  40:   
  41:          private Entity[] getEntities()
  42:          {
  43:              XmlDocument document = new XmlDocument();
  44:              document.Load(Gobal.EntityXmlFileName);
  45:              return EntityXmlParser.ParseXml(document);
  46:          }
  47:      }
  48:  }
 

这个类中定义的个单元测试,并不测试任何功能代码,而是用于生成数据库的建库脚本。VS.Net中似乎没有Make这样的自动化脚本,使用第三方的组件和工具则略显繁琐,而这种单元测试的“歪用”能起到不错的效果。:)

单元测试充当自动化脚本还可以实现很多功能,常用的除了上面的生成数据库脚本外,还有生成实体类代码、生成Linq的外置XML映射文件、填充测试数据、从旧版本系统中导入数据、统计代码行数等,大家可以尽情的发挥想象,呵呵。

回到我们的系统中,使用Debug的方式运行Util_CreateDatabaseScript测试,数据库脚本会写入到Output窗口中(使用快捷键Ctrl+W、O打开)。

   1:  if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Blog_BlogClass]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
   2:  ALTER TABLE [dbo].[Blog] DROP CONSTRAINT FK_Blog_BlogClass
   3:  GO
   4:   
   5:  if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Blog]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
   6:  drop table [dbo].[Blog]
   7:  GO
   8:   
   9:  if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[BlogClass]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
  10:  drop table [dbo].[BlogClass]
  11:  GO
  12:   
  13:  CREATE TABLE [dbo].[Blog] (
  14:      [ID] [int] IDENTITY (1, 1)  NOT NULL ,
  15:      [Title] [nvarchar] (255) COLLATE Chinese_PRC_CI_AS NOT NULL ,
  16:      [Content] [text] COLLATE Chinese_PRC_CI_AS ,
  17:      [BlogClassID] [int] ,
  18:      [CreateDateTime] [datetime] NOT NULL ,
  19:      [UpdateDateTime] [datetime] NOT NULL ,
  20:      [TimeStamp] [timestamp] NULL
  21:  ) ON [PRIMARY]
  22:  GO
  23:   
  24:  CREATE TABLE [dbo].[BlogClass] (
  25:      [ID] [int] IDENTITY (1, 1)  NOT NULL ,
  26:      [Name] [nvarchar] (255) COLLATE Chinese_PRC_CI_AS NOT NULL ,
  27:      [Description] [nvarchar] (255) COLLATE Chinese_PRC_CI_AS ,
  28:      [TimeStamp] [timestamp] NULL
  29:  ) ON [PRIMARY]
  30:  GO
  31:   
  32:  ALTER TABLE [dbo].[Blog] WITH NOCHECK ADD
  33:      CONSTRAINT [PK_Blog] PRIMARY KEY  CLUSTERED
  34:      (
  35:          [ID]
  36:      )  ON [PRIMARY]
  37:  GO
  38:  ALTER TABLE [dbo].[BlogClass] WITH NOCHECK ADD
  39:      CONSTRAINT [PK_BlogClass] PRIMARY KEY  CLUSTERED
  40:      (
  41:          [ID]
  42:      )  ON [PRIMARY]
  43:  GO
  44:  ALTER TABLE [dbo].[Blog] WITH NOCHECK  ADD 
  45:      CONSTRAINT [FK_Blog_BlogClass] FOREIGN KEY 
  46:      (
  47:          [BlogClassID]
  48:      ) REFERENCES [dbo].[BlogClass] (
  49:          [ID]
  50:      ) 
  51:  GO

这里数据库脚本的生成借鉴了ROR的一些优点,比如为每个实体添加ID主键和时间戳。另外,实体数据字段类型对应到了具体的数据库类型(bool->bit,text->nvarchar(255)等),而实体间关系对应到表间关系。至于具体的对应规则和生成方式,可以根据项目和所采用数据库的不同进行调整。

自动化的优点就在于,每次我们修改实体XML后,只要重新生成一遍数据库脚本,就可以完成数据库的更新,这点在团队开发中很有用,任何人修改数据库都只需要通知同伴更新一下即可。而在项目每一轮迭代完成、进行发布的时候,也可以方便的获取数据库脚本。

生成器的代码在YD.Data.EntityPrase和YD.Data.DatabaseScriptGenerater这两个命名空间下。很简单,无非是拼字符串而已,大家可以参考一下,根据自己的需要编写自己的生成器(我一直认为编写代码生成工具是程序员的基本功)。

下一篇文章我们将研究怎么生成实体类以及要生成什么样的实体类。

代码下载