您的位置:首页 > 编程语言

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

2017-05-09 17:46 267 查看
原文:30分钟教您打造自己的代码生成器

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

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

一、前言背景(3分钟)

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

二、技术储备[b](2分钟)[/b]

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

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

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

三、开始构建(20分钟)

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

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

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Windows.Forms;

namespace WindowsTest
{
public partial class CodeCreate : Form
{
private static string S_conStr = "Data Source=127.0.0.1;Initial Catalog=master;Integrated Security=False;user=sa;password=******;";

public CodeCreate()
{
InitializeComponent();
}

public static DataTable ExcuteQuery(string connectionString, string cmdText, List<SqlParameter> pars = null)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand(cmdText, conn))
{
if (pars != null && pars.Count > 0) cmd.Parameters.AddRange(pars.ToArray());
using (SqlDataAdapter adp = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
adp.Fill(dt);
return dt;
}
}
}
}

private void CodeCreate_Load(object sender, EventArgs e)
{
#region 获取所有数据库的信息
string sql = @" SELECT *
FROM master..sysdatabases
where dbid>6
ORDER BY dbid";
#endregion

DataTable dt = ExcuteQuery(S_conStr, sql);
this.treeView1.Nodes.Add("192.168.30.69");
foreach (DataRow dr in dt.Rows)
{
this.treeView1.Nodes[0].Nodes.Add(dr["name"].ToString());
}

this.treeView1.ExpandAll();
}

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
this.tabControl1.SelectedIndex = 0;

if (e.Node.Level == 0) return;

#region 获取数据库所有表字段信息的sql
string sql = @"
select
[表名]=c.Name,
[表说明]=isnull(f.[value],''),
[列序号]=a.Column_id,
[列名]=a.Name,
[列说明]=isnull(e.[value],''),
[数据库类型]=b.Name,
[类型]= case when b.Name = 'image' then 'byte[]'
when b.Name in('image','uniqueidentifier','ntext','varchar','ntext','nchar','nvarchar','text','char') then 'string'
when b.Name in('tinyint','smallint','int','bigint') then 'int'
when b.Name in('datetime','smalldatetime') then 'DateTime'
when b.Name in('float','decimal','numeric','money','real','smallmoney') then 'decimal'
when b.Name ='bit' then 'bool' else b.name end ,
[标识]= case when is_identity=1 then '是' else '' end,
[主键]= case when exists(select 1 from sys.objects x join sys.indexes y on x.Type=N'PK' and x.Name=y.Name
join sysindexkeys z on z.ID=a.Object_id and z.indid=y.index_id and z.Colid=a.Column_id)
then '是' else '' end,
[字节数]=case when a.[max_length]=-1 and b.Name!='xml' then 'max/2G'
when b.Name='xml' then '2^31-1字节/2G'
else rtrim(a.[max_length]) end,
[长度]=case when ColumnProperty(a.object_id,a.Name,'Precision')=-1 then '2^31-1'
else rtrim(ColumnProperty(a.object_id,a.Name,'Precision')) end,
[小数位]=isnull(ColumnProperty(a.object_id,a.Name,'Scale'),0),
[是否为空]=case when a.is_nullable=1 then '是' else '' end,
[默认值]=isnull(d.text,'')
from
sys.columns a
left join
sys.types b on a.user_type_id=b.user_type_id
inner join
sys.objects c on a.object_id=c.object_id and c.Type='U'
left join
syscomments d on a.default_object_id=d.ID
left join
sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1
left join
sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1 ";
#endregion

if (e.Node.Level == 1)
{
DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Text), sql);
this.dataGridView1.DataSource = dt;

if (dt.Rows.Count > 0)
{
e.Node.Nodes.Clear();
DataRow[] aryDr = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(aryDr, 0);
List<string> listTableName = aryDr.Select(a => a["表名"].ToString()).Distinct().ToList();
listTableName.ForEach(a =>
{
e.Node.Nodes.Add(a, a);
});
e.Node.ExpandAll();
}
}

if (e.Node.Level == 2)
{
sql += "where c.Name=@TableName ";
List<SqlParameter> listSqlParameter = new List<SqlParameter>()
{
new SqlParameter("@TableName",e.Node.Text),
};
DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Parent.Text), sql, listSqlParameter);
if (dt.Columns.Count > 0)
{
if (dt.Rows.Count > 0)
{
e.Node.Tag = dt.Rows[0]["表说明"].ToString();
}
dt.Columns.Remove("表名");
dt.Columns.Remove("表说明");
}
this.dataGridView1.DataSource = dt;
}
}

private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.tabControl1.SelectedTab.Text == "Model")
{
if (this.treeView1.SelectedNode.Level == 2 && this.dataGridView1.Rows.Count > 0)
{
string modelTmp = @"
using System;

namespace #ModelNamespace#
{
/// <summary>
/// #ModelClassDescription#
/// Create By Tool #CreateDateTime#
/// </summary>
public class #ModelClassName#
{
#PropertyInfo#
}
}";

string modelNamespace = "Model";
string modelClassName = this.treeView1.SelectedNode.Text;

string propertyInfo = string.Empty;
foreach (DataGridViewRow dgvr in this.dataGridView1.Rows)
{
string modelPropertyTmp = @"
/// <summary>
/// #PropertyDescription#
/// </summary>
public  #PropertyType# #PropertyName# { get; set; }";

string propertyDescription = dgvr.Cells["列说明"].Value.ToString().Trim();
string propertyName = dgvr.Cells["列名"].Value.ToString().Trim();
string propertyType = dgvr.Cells["类型"].Value.ToString().Trim();

propertyInfo += modelPropertyTmp.Replace(" #PropertyDescription#", propertyDescription)
.Replace(" #PropertyType#", propertyType)
.Replace("#PropertyName#", propertyName);
propertyInfo += Environment.NewLine;
}

modelTmp = modelTmp.Replace("#ModelClassDescription#", this.treeView1.SelectedNode.Tag.ToString())
.Replace("#CreateDateTime#", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
.Replace("#ModelNamespace#", modelNamespace)
.Replace("#ModelClassName#", modelClassName)
.Replace("#PropertyInfo#", propertyInfo);

this.richTextBox1.Text = modelTmp;
Clipboard.SetDataObject(this.richTextBox1.Text);
}
}
}
}
}

主要代码


示例代码

四、结语思考(5分钟)

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