30分钟教您打造自己的代码生成器

原文:30分钟教您打造自己的代码生成器

     

      哇咔咔,离上次写文又有几个月了。不过上次写的《这些开源项目,你都知道吗?(持续更新中...)[原创]》受到广大读者的赞赏和推荐,也使得自己更有动力去写技术文章。好了,废话不多说,直接切入正题。

      前方高能预警,由于此篇博文适合初学者入门或者各种大牛老男孩怀念那些年我们一起写过的代码生成器,所以请各路时间宝贵的大神慎重查看此篇博文,误入者后果自负!!! 

一、前言背景(3分钟)

      博主清晰的记得,大三的时候,老师会布置各种课外作业,不过大多是基于数据库增删改查的XX管理系统。此类系统,想必大家伙都懂,自然是无法避免的要编写很多重复性的简单代码。基于XX表的DAL、BLL,其实都不必用三层架构,直接两层多清爽。不过学生时代很多时候都是为了学习而生搬硬套。不像现在各种ORM,写个锤子的DAL。至于大名鼎鼎的T4,用起来似乎智能提示不怎么友好,另外就是针对特性化需求实现起来也麻烦。还有就是各种各样成熟的代码生成器(比如动软),也始终难以满足团队内部的代码生成规则。再加上那时的博主本着一颗造轮子的心,开始脑海中浮现,是否可以做一个工具?主要用以实现对应数据库中表的基础代码的自动生成,包括生成属性、增删改查、实体类等基础代码片断。使开发人员可以节省大量机械式编写代码的时间和重复劳动,而将精力集中于核心业务逻辑的开发。从而使得开发人员能够快速开发项目,缩短开发周期,减少开发成本,大大提高了项目的研发效率,使得开发人员在同样的时间创造出更大的价值。

 

二、技术储备(2分钟)

1、既然要生成数据库表所对应的代码,那么想必得从数据库获取相关表和字段的基础信息吧?

2、有了相关表和字段的基础信息了,怎么样按照特性化需求和规则生成对应的代码呢?

3、生成的代码如何展现?直接打印到界面还是生成代码文件呢?

 

三、开始构建(20分钟)

此例子,博主将使用SQL Server 2008 R2 做数据库,使用Winform做工具的UI展示。

1、执行以下sql,即可从SQL server 数据库得到相关表和字段的基础信息(SQL Server 2008 R2 亲测有效)

1 SELECT *
2 FROM master..sysdatabases

获取一个连接上的所有数据库信息

 1 select
 2     [表名]=c.Name,
 3     [表说明]=isnull(f.[value],''),
 4     [列序号]=a.Column_id,
 5     [列名]=a.Name,
 6     [列说明]=isnull(e.[value],''),
 7     [数据库类型]=b.Name,
 8     [类型]= case when b.Name = 'image' then 'byte[]'
 9                  when b.Name in('image','uniqueidentifier','ntext','varchar','ntext','nchar','nvarchar','text','char') then 'string'
10                  when b.Name in('tinyint','smallint','int','bigint') then 'int'
11                  when b.Name in('datetime','smalldatetime') then 'DateTime'
12                  when b.Name in('float','decimal','numeric','money','real','smallmoney') then 'decimal'
13                  when b.Name ='bit' then 'bool' else b.name end ,
14     [标识]= case when is_identity=1 then '是' else '' end,
15     [主键]= case when exists(select 1 from sys.objects x join sys.indexes y on x.Type=N'PK' and x.Name=y.Name
16                         join sysindexkeys z on z.ID=a.Object_id and z.indid=y.index_id and z.Colid=a.Column_id)
17                     then '是' else '' end,
18     [字节数]=case when a.[max_length]=-1 and b.Name!='xml' then 'max/2G'
19                   when b.Name='xml' then '2^31-1字节/2G'
20                   else rtrim(a.[max_length]) end,
21     [长度]=case when ColumnProperty(a.object_id,a.Name,'Precision')=-1 then '2^31-1'
22                 else rtrim(ColumnProperty(a.object_id,a.Name,'Precision')) end,
23     [小数位]=isnull(ColumnProperty(a.object_id,a.Name,'Scale'),0),
24     [是否为空]=case when a.is_nullable=1 then '是' else '' end,
25     [默认值]=isnull(d.text,'')
26 from
27     sys.columns a
28 left join
29     sys.types b on a.user_type_id=b.user_type_id
30 inner join
31     sys.objects c on a.object_id=c.object_id and c.Type='U'
32 left join
33     syscomments d on a.default_object_id=d.ID
34 left join
35     sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1
36 left join
37     sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1
38
39 获取数据库所有表字段信息

获取一个数据库上的所有表字段信息

2、构建自定义模板,然后替换模板中的标识符

说到自定义模板,无非就是一堆字符串,那这一堆字符串究竟是用XML存储还是TXT文本存储还是其他方式呢?好吧,咱不纠结到底采用哪种存储介质了。基于本文的“30分钟”,博主决定短平快,直接硬编码写死吧!用字符串对象存储起来。 

 1 string modelTmp = @"
 2 using System;
 3
 4 namespace #ModelNamespace#
 5 {
 6     /// <summary>
 7     /// #ModelClassDescription#
 8     /// Create By Tool #CreateDateTime#
 9     /// </summary>
10     public class #ModelClassName#
11     {
12 #PropertyInfo#
13     }
14 }";
15
16 实体类模板

实体类模板

1 string modelPropertyTmp = @"
2          /// <summary>
3          /// #PropertyDescription#
4          /// </summary>
5          public  #PropertyType# #PropertyName# { get; set; }";

实体类属性模板

 1 propertyInfo += modelPropertyTmp.Replace(" #PropertyDescription#", propertyDescription)
 2                                                         .Replace(" #PropertyType#", propertyType)
 3                                                         .Replace("#PropertyName#", propertyName);
 4
 5
 6 modelTmp = modelTmp.Replace("#ModelClassDescription#", this.treeView1.SelectedNode.Tag.ToString())
 7                                        .Replace("#CreateDateTime#", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
 8                                        .Replace("#ModelNamespace#", modelNamespace)
 9                                        .Replace("#ModelClassName#", modelClassName)
10                                        .Replace("#PropertyInfo#", propertyInfo);
11
12 标识符替换

替换模板中的标识符

3、采用文本控件将生成的代码打印出来

由于本文旨在让各位读者了解代码生成的原理,故博主花了10多分钟写了一个简单的代码生成器,代码自然有些粗糙,请各位见谅!

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Data;
  4 using System.Data.SqlClient;
  5 using System.Linq;
  6 using System.Windows.Forms;
  7
  8 namespace WindowsTest
  9 {
 10     public partial class CodeCreate : Form
 11     {
 12         private static string S_conStr = "Data Source=127.0.0.1;Initial Catalog=master;Integrated Security=False;user=sa;password=******;";
 13
 14         public CodeCreate()
 15         {
 16             InitializeComponent();
 17         }
 18
 19         public static DataTable ExcuteQuery(string connectionString, string cmdText, List<SqlParameter> pars = null)
 20         {
 21             using (SqlConnection conn = new SqlConnection(connectionString))
 22             {
 23                 using (SqlCommand cmd = new SqlCommand(cmdText, conn))
 24                 {
 25                     if (pars != null && pars.Count > 0) cmd.Parameters.AddRange(pars.ToArray());
 26                     using (SqlDataAdapter adp = new SqlDataAdapter(cmd))
 27                     {
 28                         DataTable dt = new DataTable();
 29                         adp.Fill(dt);
 30                         return dt;
 31                     }
 32                 }
 33             }
 34         }
 35
 36         private void CodeCreate_Load(object sender, EventArgs e)
 37         {
 38             #region 获取所有数据库的信息
 39             string sql = @" SELECT *
 40                             FROM master..sysdatabases
 41                             where dbid>6
 42                             ORDER BY dbid";
 43             #endregion
 44
 45             DataTable dt = ExcuteQuery(S_conStr, sql);
 46             this.treeView1.Nodes.Add("192.168.30.69");
 47             foreach (DataRow dr in dt.Rows)
 48             {
 49                 this.treeView1.Nodes[0].Nodes.Add(dr["name"].ToString());
 50             }
 51
 52             this.treeView1.ExpandAll();
 53         }
 54
 55         private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
 56         {
 57             this.tabControl1.SelectedIndex = 0;
 58
 59             if (e.Node.Level == 0) return;
 60
 61             #region 获取数据库所有表字段信息的sql
 62             string sql = @"
 63 select
 64     [表名]=c.Name,
 65     [表说明]=isnull(f.[value],''),
 66     [列序号]=a.Column_id,
 67     [列名]=a.Name,
 68     [列说明]=isnull(e.[value],''),
 69     [数据库类型]=b.Name,
 70     [类型]= case when b.Name = 'image' then 'byte[]'
 71                  when b.Name in('image','uniqueidentifier','ntext','varchar','ntext','nchar','nvarchar','text','char') then 'string'
 72                  when b.Name in('tinyint','smallint','int','bigint') then 'int'
 73                  when b.Name in('datetime','smalldatetime') then 'DateTime'
 74                  when b.Name in('float','decimal','numeric','money','real','smallmoney') then 'decimal'
 75                  when b.Name ='bit' then 'bool' else b.name end ,
 76     [标识]= case when is_identity=1 then '是' else '' end,
 77     [主键]= case when exists(select 1 from sys.objects x join sys.indexes y on x.Type=N'PK' and x.Name=y.Name
 78                         join sysindexkeys z on z.ID=a.Object_id and z.indid=y.index_id and z.Colid=a.Column_id)
 79                     then '是' else '' end,
 80     [字节数]=case when a.[max_length]=-1 and b.Name!='xml' then 'max/2G'
 81                   when b.Name='xml' then '2^31-1字节/2G'
 82                   else rtrim(a.[max_length]) end,
 83     [长度]=case when ColumnProperty(a.object_id,a.Name,'Precision')=-1 then '2^31-1'
 84                 else rtrim(ColumnProperty(a.object_id,a.Name,'Precision')) end,
 85     [小数位]=isnull(ColumnProperty(a.object_id,a.Name,'Scale'),0),
 86     [是否为空]=case when a.is_nullable=1 then '是' else '' end,
 87     [默认值]=isnull(d.text,'')
 88 from
 89     sys.columns a
 90 left join
 91     sys.types b on a.user_type_id=b.user_type_id
 92 inner join
 93     sys.objects c on a.object_id=c.object_id and c.Type='U'
 94 left join
 95     syscomments d on a.default_object_id=d.ID
 96 left join
 97     sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1
 98 left join
 99     sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1 ";
100             #endregion
101
102             if (e.Node.Level == 1)
103             {
104                 DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Text), sql);
105                 this.dataGridView1.DataSource = dt;
106
107                 if (dt.Rows.Count > 0)
108                 {
109                     e.Node.Nodes.Clear();
110                     DataRow[] aryDr = new DataRow[dt.Rows.Count];
111                     dt.Rows.CopyTo(aryDr, 0);
112                     List<string> listTableName = aryDr.Select(a => a["表名"].ToString()).Distinct().ToList();
113                     listTableName.ForEach(a =>
114                     {
115                         e.Node.Nodes.Add(a, a);
116                     });
117                     e.Node.ExpandAll();
118                 }
119             }
120
121             if (e.Node.Level == 2)
122             {
123                 sql += "where c.Name=@TableName ";
124                 List<SqlParameter> listSqlParameter = new List<SqlParameter>()
125                 {
126                     new SqlParameter("@TableName",e.Node.Text),
127                 };
128                 DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Parent.Text), sql, listSqlParameter);
129                 if (dt.Columns.Count > 0)
130                 {
131                     if (dt.Rows.Count > 0)
132                     {
133                         e.Node.Tag = dt.Rows[0]["表说明"].ToString();
134                     }
135                     dt.Columns.Remove("表名");
136                     dt.Columns.Remove("表说明");
137                 }
138                 this.dataGridView1.DataSource = dt;
139             }
140         }
141
142         private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
143         {
144             if (this.tabControl1.SelectedTab.Text == "Model")
145             {
146                 if (this.treeView1.SelectedNode.Level == 2 && this.dataGridView1.Rows.Count > 0)
147                 {
148                     string modelTmp = @"
149 using System;
150
151 namespace #ModelNamespace#
152 {
153     /// <summary>
154     /// #ModelClassDescription#
155     /// Create By Tool #CreateDateTime#
156     /// </summary>
157     public class #ModelClassName#
158     {
159 #PropertyInfo#
160     }
161 }";
162
163                     string modelNamespace = "Model";
164                     string modelClassName = this.treeView1.SelectedNode.Text;
165
166                     string propertyInfo = string.Empty;
167                     foreach (DataGridViewRow dgvr in this.dataGridView1.Rows)
168                     {
169                         string modelPropertyTmp = @"
170         /// <summary>
171         /// #PropertyDescription#
172         /// </summary>
173         public  #PropertyType# #PropertyName# { get; set; }";
174
175                         string propertyDescription = dgvr.Cells["列说明"].Value.ToString().Trim();
176                         string propertyName = dgvr.Cells["列名"].Value.ToString().Trim();
177                         string propertyType = dgvr.Cells["类型"].Value.ToString().Trim();
178
179                         propertyInfo += modelPropertyTmp.Replace(" #PropertyDescription#", propertyDescription)
180                                                         .Replace(" #PropertyType#", propertyType)
181                                                         .Replace("#PropertyName#", propertyName);
182                         propertyInfo += Environment.NewLine;
183                     }
184
185                     modelTmp = modelTmp.Replace("#ModelClassDescription#", this.treeView1.SelectedNode.Tag.ToString())
186                                        .Replace("#CreateDateTime#", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
187                                        .Replace("#ModelNamespace#", modelNamespace)
188                                        .Replace("#ModelClassName#", modelClassName)
189                                        .Replace("#PropertyInfo#", propertyInfo);
190
191                     this.richTextBox1.Text = modelTmp;
192                     Clipboard.SetDataObject(this.richTextBox1.Text);
193                 }
194             }
195         }
196     }
197 }
198
199 主要代码

示例代码

 

四、结语思考(5分钟)

怎么样,相信各位读者对代码生成有了基本认识了吧!我想普通的代码生成无非就是替换,当然你要是想做一个好一点的代码生成工具,自然是要考虑可扩展性,可维护性,健壮性等。比如可以在左侧的树形菜单那里加上下文菜单,新建连接,断开连接,刷新等扩展功能。在代码打印的区域可以增加另存为,复制等功能。还可以增加相关配置界面,例如命名空间,数据库连接,代码文件默认保存路径等等。自己做的代码生成器可以由自己一直不断的维护,针对特性化需求可以马上实现。好了,本文主要是简洁明了的让各位读者对代码生成原理有一个基本认识,毕竟就30分钟呀!

 

时间: 2024-09-10 22:51:51

30分钟教您打造自己的代码生成器的相关文章

PS 30分钟教你快速打造纸边撕裂的网格本字效

  效果图: 步骤1 我们要新建600*400的图层,具体设置如图所示哦~ 步骤2 然后打上"Wirte",设置文字的颜色为#FFE5CD,大小为140px~ 步骤3 现在我们打开图层样式,参数设置如下图所示: 投影,正常,透明度20%,角度90°,距离2px,大小1px 内发光,正常,透明度7%,颜色#000000,柔软,边缘,大小:15px 步骤4 然后捏,我们用"Ctrl+J"来复制一层文本,并把填充改变成0%~再打开图层样式设置参数~=W= 投影,正常,透明

海尔推出人工智能电视,打造智慧模块化 ;物联网医疗解决方案助力心梗病人,救治前时间已缩短至 30 分钟

海尔推出人工智能电视,打造智慧模块化  雷锋网消息,近日,行电视创业 20 周年纪念庆典,自中国电子视像行业协会.中国电子商会.奥维云网.中怡康.GFK.帕勒咨询.三星显示.阿里巴巴等公司的业内专家和行业精英共聚一堂,探讨如何更好运用人工智能技术助推彩电行业转型升级,以及人工智能电视的发展方向. 其中,海尔阿里电视成为人工智能电视的佼佼者.目前海尔阿里四代电视在人工智能应用方面有六大引擎分别是:人工智能千人千面.人工智能全过程语音控制.人工智能物联网大数据.人工智能 4K 影视 VIP.人工智能

六步,刘朋教你打造高效制胜团队的套路(加强版)

刘朋 诺基亚网络成都研发中心研发经理,成都研发中心ecosystem建设负责人,成都研发中心敏捷教练.10年+软件行业开发.团队教练和团队管理经验,在整洁代码.敏捷最佳实践和软件开发高效能团队有丰富的理论和实战经验.活跃于成都敏捷和技术社区,"天府软件园技术社区·整洁代码俱乐部"发起人,作为讲师多次参加敏捷之旅成都站.MPD成都站.中生代技术社区以及"天府软件园技术社区·整洁代码俱乐部"进行分享,并作为演讲嘉宾代表诺基亚在2015年"四川省第九届互联网大会

建站宝盒-30分钟以内建一个三站合一的企业网站

跟传统建站模式的区别:自助建站,是随着互联网的迅 速发展而产生的一种新型.便捷.傻瓜智能式的建站模式.企业自助 建站系统的出现,可以说是网站建设的一个里程碑,最早掀起第三代网站建设的先行者,企业自助建站系统可以同时为网站建设者和网站使用者带来省时省力省钱的双重效果.国内一流的自助建站系统:卓天网络的建站宝盒. &http://www.aliyun.com/zixun/aggregation/37954.html">nbsp; 我们曾经做过这样一个调查:   找一个当地的网络公司,告

在线学习注意力多为16至30分钟

用户学习行为分析报告显示互联网教育面临挑战也充满商机 本报讯 (记者张鹏)昨日发布的<2013-2014中国互联网教育用户行为分析报告>显示,虽然互联网教育前景可观,但仍面临课程体系完整性.保证用户粘度和传播渠道多元化等多方面挑战.这项调查是沪江网联合德勤.<中国经营报>及速途研究院进行的,报告调研了全国45162名学习爱好者的互联网行为习惯,统计了用户信息.学习动机等数据.本科学历者是主流用户调查显示,参与互联网教育用户基本分布地域以上海.北京.广东等发达地区为主,占到43.3%

建站宝盒-30分钟以内建出一个极具专业的企业网站

&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;   跟传统建站模式的区别:自助建站,是随着互联网的迅速发展而产生的一种新型.便捷.傻瓜智能式的建站模式.企业自助建站系统的出现,可以说是网站建设的一个里程碑,最早掀起第三代网站建设的先行者,企业自助建站系统可以同时为网站建设者和网站使用者带来省时省力省钱的双重效果.国内一流的自助建站系统:卓天网络的建站宝盒. 我们曾经做过这样一个调查: 找一个当地的网络公司,告诉他

一分钟教你调出夏日清新日系色调

  一个原创调色教程,一分钟教你调出夏日清新日系色调,真的只要一分钟,喜欢的童鞋们,点击大图学习,很高清 分类: PS调色教程

30分钟学会正则表达式基础教程

基础教程|教程|正则表达式|正则 目录 本文目标 如何使用本教程 正则表达式到底是什么? 入门 测试正则表达式 元字符 字符转义 重复 字符类 反义 替换 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 最后,来点广告... 一些我认为你可能已经知道的术语的参考 网上的资源及本文参考文献 更新说明 本文目标 30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用它. 如何使用本教程 最重要

30分钟正则表达式指导

正则 by Jim Hollenhorst  译 寒带鱼 你是否曾经想过正则表达式是什么,怎样能够快速得到对它的一个基本的认识?我的目的就是在30分钟内带你入门并且对正则表达式有一个基本的理解.事实是正则表达式并没有它看起来那么复杂.学习它最好的办法就是开始写正则表达式并且不断实践.在最初的30分钟之后,你就应该知道一些基本的结构并且有能力在你的程序或者web页面中设计和使用正则表达式了.对那些想要深入研究的人,现在已经有很多非常好的可用资源来让你更深入的学习. 到底什么是正则表达式? 我相信你