上回书所产生的实体类有啥问题?


第一:所产生的这个类,根本编译不过去。


第二:这个类的类名永远是Person,即使产生了这个类,还得修改。


第三:命名空间也要改。


第四:……


行了,我知道了,一口吃不成一个大胖子,一个一个问题的解决。


先看编译过不去的原因:已经定义了一个名字为title_id的字段了,错误提示在title_id这个属性那里。原来是字段名称和属性名称冲突了,正确的应该是字段名称使用骆驼命名法,而属性用Pascal命名法。这好办,只需要将字段的第一个字母改为小写,属性的第一个字母改为大写。能直接使用字符串的方法不?可以,但是不是太落伍了,可以考虑写一个公共函数,这样别的地方要用的话也可以嘛。


函数部分终于可以使用了。


//以骆驼命名法格式化字符串


public string ToCamel(string s)


{


       return s.Substring(0,1).ToLower()+s.Substring(1);


}


//Pascal命名法格式化字符串


public string ToPascal(string s)


{


       return s.Substring(0,1).ToUpper()+s.Substring(1);


}


更改输出的代码段:


//字段部分


private <%=column.SystemType%> <%=ToCamel(column.Name)%>;


//属性部分


public <%=column.SystemType%> <%=ToPascal(column.Name)%>


{


       get{return <%=ToCamel(column.Name)%>;}


       get{<%=ToCamel(column.Name)%>=value;}


}


第一个问题完美解决。


第二个问题:想要让类名变化,最好让类名和表名有关系。啊哈,表名能不能来做类名呢?


可以,就是又产生一个小问题,因为表名通常为复数形式,能不能改成单数啊?


这个,这个,(变态真变态,难道让我去处理不规则名词的复数到单数形式,碗大的汗珠)行,只要去掉表名后的“s”嘛,小菜一碟(此处应表面不漏声色,把糊弄进行到底,当然我们处理绝大多数的问题,绝少数问题留给上帝解决。),不就是一个函数的问题吗。瞧好了。


public string GetClassName(TableSchema table)


{


       string s=table.Name;


       if(s.Substring(s.Length-1)==”s”)


       {


              return ToPascal(s.Substring(0,s.Length-1));


       }


       return ToPascal(s);


}


将“Person”换了。


class <%=GetClassName(this.SourceTable)%>


 


第二个问题完美解决。


第三个问题:命名空间可和表名没啥关系,不过一般情况下,都将命名空间设置为Model或者Entity,可是到底选择哪一个什么?这还真是个问题,这么复杂的问题一般我们都不处理,留给用户处理,他想用哪个就是那个。在模板上加个参数,也就是加一个Property声明嘛,让他自己填,他爱填啥就是啥。


在声明部分添加:


<%@ Property Name=”NameSpace” Type=”System.String” Default=”Model” Optional=”False” Category=”” Description=”” Editor=”” EditorBase=”” Serializer=”” %>


为啥加个Default属性?


这是防万一的招,如果每个属性都让用户填写,难免没有信心继续使用,只让用户必须填写那些必须填写的内容,其他的辅助项目,给个默认值,让用户省心,也就是让我们省心,如此好事,为何不做?!


namespace换了。


namespace <%=NameSpace%>


 


第三个问题完美解决。


还有第四个呢。


下回再说。



书接上回。


上回书中所产生的实体类,编译没有问题,使用也没有问题。那你还要咋么?


可是可是毕竟看起来不太舒服。


哪里不舒服?


虽然说“System.String”和“string”都是一回事,但是我们写的程序毕竟不是用MSIL语言写的啊,是用C#啊,最好别用CTS中的数据类型,大老,你说呢?


(能提出这个问题的,一律是好娃,可是好娃就是麻烦,净提一些精益求精的问题,搞死人了。)啊,这个问题啊,先说前提,CodeSmith不能把System.String直接转换为string,不过,常用的数据类型就不多的几种,可以考虑,采用分支结构,挨个转换嘛。这就是程序员伟大的地方,知其不可而为之,而且还为的挺好的。


解决的函数如下:


public string GetCSDataType(ColumnSchema column)


{


       switch (column.DataType)


       {


              case DbType.AnsiString: return “string”;


              case DbType.AnsiStringFixedLength: return “string”;


              case DbType.Binary: return “byte[]”;


              case DbType.Boolean: return “bool”;


              case DbType.Byte: return “byte”;


              case DbType.Currency: return “decimal”;


              case DbType.Date: return “DateTime”;


              case DbType.DateTime: return “DateTime”;


              case DbType.Decimal: return “decimal”;


              case DbType.Double: return “double”;


              case DbType.Guid: return “Guid”;


              case DbType.Int16: return “short”;


              case DbType.Int32: return “int”;


              case DbType.Int64: return “long”;


              case DbType.Object: return “object”;


              case DbType.SByte: return “sbyte”;


              case DbType.Single: return “float”;


              case DbType.String: return “string”;


              case DbType.StringFixedLength: return “string”;


              case DbType.Time: return “TimeSpan”;


              case DbType.UInt16: return “ushort”;


              case DbType.UInt32: return “uint”;


              case DbType.UInt64: return “ulong”;


              case DbType.VarNumeric: return “decimal”;


              default:


              {


                     return “__UNKNOWN__” + column.NativeType;


              }


       }


}


大老啊,你你忽悠我,这里明明没有用到“System.String”和其他的CTS中的类型啊,忽悠人也不能这样啊。


小子无知,焉敢多舌。这事难道为师不知道?!为师是希望你能顿悟,没想到啊没想到,唉…………


如果咱们用“System.String”和其他的CTS中数据类型的名字是不是可以做得到呢?当然是完全可以,可是在case的后面要自行填写字符串常量,case的结果处,是不是也要返回一个字符串常量,为师一贯以实践“懒人创造了世界,世界是为懒人创造的”的信条为己任,能少写就少写,能不写就不写,这样是不是太麻烦了嘛。


无论是从列结构上获取的SystemType还是DataType属性,都是用来表达这个列的数据类型的,尽管他们分别是CTSSQL中的数据类型,从CTS转换和从SQL转换,有什么本质差别不?可是DataType是个枚举类型的属性,如果在VS.Net2005中,一旦switch一个枚举变量,可以自动生成这个枚举类型中所有的枚举值的case分支,这样,咱们只需要填写case的结构就可以了。


小子,悟了吗?要不要为师给你来个当头棒喝?


大老,明白了(敢不明白,不明白就拿大棒子敲,谁敢不明白)。


好的,那么将这个函数放在他应该在的地方,怎么样啊?


好。


经过上面几次的训练,生成实体类的模板应该可以做了。是不是只能做传统编程语言的源代码?


当然不是,如果将传统的编程语言的源代码看作普通的文本文件,那么我们前几次生成的源代码可以认为是后缀名为“.cs”的文本文件而已,既然能生成后缀为“.cs”的,那么其他的呢?


其实只要是源代码,一般都是文本文件格式的。


下面我们以一种非传统编程语言来演示CodeSmith生成内容的多样性。


一般情况下,我们是先有数据库文档,然后才有具体的数据库实现,可是现实中,并不总是这样的,很有可能我们是先设计的数据库,到了该提交工程的时候,才想起来,噢,麻烦了,数据库文档还没呢。


让程序员去写程序,问题不大,可是让他做这些重复的,机械性的劳动,不光是程序员回厌烦,其他人也是这样的,由于这个原因,人们才发明了各式各样的机器来减轻劳动强度。


可是,机器的产生光是减轻劳动强度吗?其实他还可以降低出错的概率,我常说,人有两个最大的优点:一是会犯错误,一是会遗忘。


大老,你就忽悠吧,犯错误是优点?遗忘是优点?


当然是优点了,人类社会的哪一次进步不是从错误中诞生的,没有遗忘,意味着你的脑子中充斥的是大量的无用的,过时的信息。


好了,言归正传,我们开始设计数据库文档。


按照以前的套路,先得知道生成的文档长的啥样。一般的数据库文档,无外乎表结构,视图,存储过程。


从简单开始吧,一般的表结构中包括以下几项内容:列名,数据类型,长度,精度,小数位数,是否为标识列,是否为主键,是否为外键,是否为空,有无默认值等。


一般将这些内容放入一张表格中来展示,目前展示表格的文件有很多,比如Excel、Word、Html、Xml等,但是,Excel、Word文件内容一般为二进制格式,不是纯粹的文本文件,因而可以选择的文件格式为Html和Xml,相对来说Html对格式的要求不严格,因而我们准备将我们生成数据库文档设置为Html格式。


在Html中也有表格,不过是以标记的方式展示的。样子应该象下图:






















































































authors
列名 类型 长度 是否为空 主键 外键 是否标识列
au_id char 11   Y    
au_lname varchar 40        
au_fname varchar 20        
phone char 12        
address varchar 40 Y      
city varchar 20 Y      
state char 2 Y      
zip char 5 Y      
contract bit 1        



在这个需求中,我们要使用DatabaseSchema,TableSchema,ColumnSchema,在数据库结构中有表结构的集合,在表结构中有列结构的集合,这样,我们可以用两个循环嵌套的方式,生成所有的表的文档。


<%@ CodeTemplate Language=”C#” TargetLanguage=”Html” Src=”” Inherits=”” Debug=”False” Description=”Template description here.”  ResponseEncoding=”UTF-8″%>
<%@ Property Name=”Developer” Type=”String” Default=”” Optional=”False” Category=”Context” Description=”作者” OnChanged=”” Editor=”” EditorBase=”” Serializer=”” %>
<%@ Property Name=”SourceDB” Type=”SchemaExplorer.DatabaseSchema” Default=”” Optional=”False” Category=”Context” Description=”数据库名称” OnChanged=”” Editor=”” EditorBase=”” Serializer=”” %>


<%@ Assembly Name=”SchemaExplorer” %>
<%@ Assembly Name=”System.Data” %>
<%@ Import Namespace=”System.Data” %>
<%@  Import Namespace=”SchemaExplorer” %>
<%– 类生成内容在此–%>
<html>
<head><title><%=this.SourceDB.Name%></title></head>
<style>
 .header{background-color:#336699;color:white;}
 
</style>
<body>
<%foreach(TableSchema table in SourceDB.Tables){%>
 <table border=”1″ width=”92%”>
  <caption class=”header”><%=table.Name%></caption>
  <tr>
   <th class=”header”>列名</th>
   <th class=”header”>类型</th>
   <th class=”header”>长度</th>
   <th class=”header”>是否为空</th>
   <th class=”header”>主键</th>
   <th class=”header”>外键</th>
   <th class=”header”>是否标识列</th>
  </tr>
  <%foreach(ColumnSchema column in table.Columns){%>
   <tr>
    <td><%=column.Name%></td>
    <td><%=column.NativeType%></td>
    <td><%=column.Size%></td>
    <td><%=IsNull(column)%></td>
    <td><%=IsPK(column)%></td>
    <td><%=IsFK(column)%></td>
    <td><%=IsID(column)%></td>
   </tr>
  <%}%>  
 </table>
<%}%>
</body>
</html>
<script runat=”template”>
public string IsID(ColumnSchema column)
{
 if((bool)column.ExtendedProperties[“CS_IsIdentity”].Value)
 {
  return “Y”;
 }
 return “&nbsp;”;
}
public string IsNull(ColumnSchema column)
{
 if(column.AllowDBNull) 
 {
  return “Y”;
 }
 return “&nbsp;”;
}
public string IsPK(ColumnSchema column)
{
 if(column.IsPrimaryKeyMember) 
 {
  return “Y”;
 }
 return “&nbsp;”;
}
public string IsFK(ColumnSchema column)
{
 if(column.IsForeignKeyMember) 
 {
  return “Y”;
 }
 return “&nbsp;”;
}
public string GetTableName(TableSchema table)
{
 return table.Name;
}
public string GetColumnName(ColumnSchema column)
{
 return column.Name; 
}
public bool HasLength(ColumnSchema column)
{
 switch(column.NativeType)
 {
  case “char”:
  case “varchar”:
  case “nchar”:
  case “nvarchar”:
  case “varbinary”:
  case “binary”:
   return true;
  default:
   return false;
 }
}



</script>


转自 http://blog.sina.com.cn/s/articlelist_1229294631_0_1.html

Comments are closed.

Post Navigation