原文:小程序大智慧,sqlserver 注释提取工具

开篇背景

我习惯在写表的创建脚本时将注释直接写在脚本里,比如

/*账套*/
CREATE TABLE [dbo].[AccountingBook]
(
[IDNO] NVARCHAR (255) NOT NULL, /*ID*/
[BH] NVARCHAR (255) NULL, /*业务编号*/
[Name] NVARCHAR (255) NOT NULL, /*名称*/
[Decription] NVARCHAR (255) NULL, /*描述*/
[Owner] NVARCHAR (255) NOT NULL, /*所属*/
CONSTRAINT [PK_AccountingBook] PRIMARY KEY CLUSTERED ([IDNO] ASC)
)

这样写很直观,如果在vs里创建一个数据库项目,把表的创建脚本放在里面进行管理,就非常方便的。

由于习惯用自己的Orm框架,所以DTO也就是那些数据映射实体我都是用codeSmith生成,生成这些DTO对象时,我想共用我的那些注释,那么我该怎么办呢,之前,我需要把这些注释复制出来写成一些注释创建的脚本,像这样  

exec sp_addextendedproperty N'MS_Description', N'字段描述', N'user', N'dbo', N'table', N'表名', N'column', N'字段名'

添加注释的目的是除了在使用数据库连接工具时方便查看表和字段的说明外,还可以使用CodeSmith生成代码的时候就可以通过编写模版生成带注释的映射DTO 对象,如下  

/// <summary>
/// 业务编号
/// </summary>
[Column(ColumnName=Columns.BH,FullName=Columns.BHFullName,Index=,CType=typeof(string),Description="业务编号")]
[DataMember(Order = )]
public virtual string BH{get;set;}

但是由于表创建脚本里的注释不能直接写入到数据库的表和字段中,所以注释的创建脚本我需要再写一次,我觉得比较不爽,于是我决定写个小工具从表的创建脚本里面抽取那些本来就写好的注释,从而减小重复机械的工作,也防止错误的发生。

这样一个程序非常简单,下面说一下思路

实现

一,写好带注释的表脚本,并提取这些信息

格式按文章开始讲到的那样写好,即"/*注释*/",“*”可以是多个,比如"/*********注释***********/",并将这些脚本存放到相同根目录

要提取这些注释,最大的功臣非正则表达式莫属了

    

a.提取表头的注释,也就是表名的解释正则表达式      

 private readonly Regex _tableReg = new Regex(@"/[\*]+([^\*]*)[\*]+/[\r\n\s]*CREATE TABLE \[dbo\].\[(\w*)\]");

b.提取列的注释正则表达式

private readonly Regex _columnsReg = new Regex(@"\[([\w]*)\][^\/]*/[\*]+([^\*]*)[\*]+/");

二,递归查找到这些表的创建脚本,将每个生成注释脚本的字符串连接起来

Func<string, List<string>> getFolderSqlNotes = null;
getFolderSqlNotes = path =>
{
var listAll = new List<string>();
var files = Directory.GetFiles(path);
var dirs = Directory.GetDirectories(path);
foreach (string t in dirs)
{
listAll.AddRange(getFolderSqlNotes(t)); ;
}
var listStr = files.Where(m => m.EndsWith(".sql")).Select(GetDescriptionSql).ToList();
listAll.AddRange(listStr);
return listAll;
};
var list = getFolderSqlNotes(Path);
return string.Join("\r\n", list);

三,执行生成好的脚本(如下图),注释创建完成

四,用codesimth 生成映射类(已带注释)

核心代码

 namespace GenrerateSqlDescription
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions; public class SqlNotesGenerator
{
private readonly Regex _tableReg = new Regex(@"/[\*]+([^\*]*)[\*]+/[\r\n\s]*CREATE TABLE \[dbo\].\[(\w*)\]");
private readonly Regex _columnsReg = new Regex(@"\[([\w]*)\][^\/]*/[\*]+([^\*]*)[\*]+/");
private const string TableDescriptionCrateSqlFormat = "EXEC sp_addextendedproperty 'MS_Description','{0}','user ',dbo,'table',[{1}];\r\n";
private const string ColumnDescriptionCrateSqlFormat = "EXEC sp_addextendedproperty 'MS_Description','{0}','user',dbo,'table',[{1}],'column','{2}';\r\n";
private const string TableDescriptionDropSqlFormat = "EXEC sp_dropextendedproperty 'MS_Description','user',dbo,'table',[{0}];\r\n";
private const string ColumnescriptionDropSqlFormat = "EXEC sp_dropextendedproperty 'MS_Description','user',dbo,'table',[{0}],'column','{1}';\r\n";
private const string CheckTableExistsSqlFormat = "IF OBJECT_ID(N'{0}',N'U') IS NOT NULL \r\nBEGIN \r\n";
private const string EndStr = "END";
private const string Tab = " "; public bool GenDrop { get; set; }
public string Path { get; set; } private string GetDescriptionSql(string path)
{
var sb = new StringBuilder();
var fs = File.OpenRead(path);
var fileRd = new StreamReader(fs);
var str = fileRd.ReadToEnd();
var tableMatch = _tableReg.Match(str);
if (tableMatch.Length < ) return string.Empty; var tableName = tableMatch.Groups[].Value;
var tableDes = tableMatch.Groups[].Value;
if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(tableDes)) return string.Empty; var columnStr = str.Substring(str.IndexOf("(", StringComparison.Ordinal));
var matches = _columnsReg.Matches(columnStr); sb.AppendFormat(CheckTableExistsSqlFormat, tableName); sb.Append(Tab);
if (GenDrop)
sb.AppendFormat(TableDescriptionDropSqlFormat, tableName);
else
sb.AppendFormat(TableDescriptionCrateSqlFormat, tableDes, tableName);
foreach (Match match in matches)
{
var columnName = match.Groups[].Value;
var description = match.Groups[].Value;
if (string.IsNullOrEmpty(columnName) || string.IsNullOrEmpty(description))
continue;
sb.Append(Tab);
if (GenDrop)
sb.AppendFormat(ColumnescriptionDropSqlFormat, tableName, columnName);
else
sb.AppendFormat(ColumnDescriptionCrateSqlFormat, description, tableName, columnName);
} sb.AppendLine(EndStr);
return sb.ToString();
} public string GetGenerateSql()
{
Func<string, List<string>> getFolderSqlNotes = null;
getFolderSqlNotes = path =>
{
var listAll = new List<string>();
var files = Directory.GetFiles(path);
var dirs = Directory.GetDirectories(path);
foreach (string t in dirs)
{
listAll.AddRange(getFolderSqlNotes(t)); ;
}
var listStr = files.Where(m => m.EndsWith(".sql")).Select(GetDescriptionSql).ToList();
listAll.AddRange(listStr);
return listAll;
};
var list = getFolderSqlNotes(Path);
return string.Join("\r\n", list);
} } }

SqlNotesGenerator

CodeSimith模版

 <%--
Name:
Author:
Description:
--%>
<%@ CodeTemplate Language="C#" TargetLanguage="C#" Src="" Inherits="OutputFileCodeTemplate" Debug="False" Description="Template description here." %>
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Optional="True" Description="the table name" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>
<%@ Assembly Name="CodeSmith.BaseTemplates" %>
<%@ Import Namespace="CodeSmith.BaseTemplates" %>
<%@ Assembly Name="CodeSmith.CustomProperties" %>
<%@ Import Namespace="CodeSmith.CustomProperties" %>
//------------------------------------------------------------------------------
// <auto-generated>
// This code generated by the tool, do not propose to amend
// Generation time:<%=DateTime.Now%>
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Data;
using System.Runtime.Serialization;
using XDbFramework;
using BPM_M01.DataAccessLayer.Base;
using System.Xml.Serialization;
using System.Diagnostics;
using System.CodeDom.Compiler; namespace <%=Namespace%>
{
[Table(TableName = <%=this.SourceTable.Name%>.Table.Name ,Descripton = "<%=this.SourceTable.Description%>",GenreratePK = <%=GenreratePK.ToString().ToLower()%>)]
[GeneratedCodeAttribute("System.Xml", "2.0.50727.4927")]
[DebuggerStepThroughAttribute()]
public partial class <%=this.SourceTable.Name%> : DtoBase
{
public static class Table
{
public const string Name = "<%=this.SourceTable.Name%>";
}
public static class Columns
{
<%for(int i=; i<this.SourceTable.Columns.Count;i++)
{
string colName=this.SourceTable.Columns[i].Name;%>
public const string <%=colName%> = "<%=colName%>";
public const string <%=colName%>FullName =Table.Name + "." + "<%=colName%>";
<%}%>
} <%for(int i=; i<this.SourceTable.Columns.Count;i++)
{
int namelength=this.SourceTable.Columns[i].Name.Length;
string colName=this.SourceTable.Columns[i].Name;
if(colName == "ID" || colName == "IDNO") continue;
string titleCaseColumName = colName.Substring(,).ToUpper() + colName.Substring(,colName.Length-);
%> /// <summary>
/// <%=this.SourceTable.Columns[i].Description%>
/// </summary>
<%if(this.SourceTable.Columns[i].IsPrimaryKeyMember){%>
[Column(KeyType = KeyTypeEnum.PrimaryKey,FullName="<%=this.SourceTable.Name%>.<%=this.SourceTable.Columns[i].Name%>", ColumnName="<%=this.SourceTable.Columns[i].Name%>",Index=<%=i%>,Description="<%=this.SourceTable.Columns[i].Description%>")]
<%}%>
<%else{%>
<%if(this.SourceTable.Columns[i].IsForeignKeyMember)%>
<%//
foreach(TableKeySchema tks in SourceTable.ForeignKeys)
{
// foreach(MemberColumnSchema mcs in tks.ForeignKeyMemberColumns)
{ //
if(mcs.Name == SourceTable.Columns[i].Name)
{
TableSchema ts= tks.PrimaryKeyTable;%>
[Column(ColumnName=Columns.<%=colName%>,KeyType = KeyTypeEnum.ForeignKey,FullName=Columns.<%=colName%>FullName,CType=typeof(<%=GetCSharpVariableType(this.SourceTable.Columns[i])%>),ForeignKeyTableName="<%=ts.Name%>",ForeignKeyFiledName="<%=ts.PrimaryKey.MemberColumns[0].Name%>", DbType=<%=GetSqlDBType(this.SourceTable.Columns[i].NativeType)%> Index=<%=i%>,Description="<%=this.SourceTable.Columns[i].Description%>")]
<% break;
}
}
}
%>
<%else{%>
[Column(ColumnName=Columns.<%=colName%>,FullName=Columns.<%=colName%>FullName,Index=<%=i%>,CType=typeof(<%=GetCSharpVariableType(this.SourceTable.Columns[i])%>),Description="<%=this.SourceTable.Columns[i].Description%>")]
<%}%>
<%}%>
[DataMember(Order = <%=i%>)]
public virtual <%=GetCSharpVariableType(this.SourceTable.Columns[i])%> <%=titleCaseColumName%>{get;set;} <%}%>
}
} <script runat="template">
// Override the OutputFile property and assign our specific settings to it.
[FileDialog(FileDialogType.Save, Title="Select Output File", Filter="C# Files (*.cs)|*.cs", DefaultExtension=".cs")]
public override string OutputFile
{
get {return base.OutputFile;}
set {base.OutputFile = value;}
}
private string _Namespace;
public string Namespace
{
get{return _Namespace;}
set{_Namespace = value;}
} public bool GenreratePK{get;set;}
</script>
<script runat="template">
public string GetSqlDBType(string type)
{
switch(type)
{
case "int": return "SqlDbType.Int,";
case "bit": return "SqlDbType.Bit,";
case "bigint": return "SqlDbType.BigInt,";
case "tinyint": return "SqlDbType.TinyInt,";
case "nvarchar": return "SqlDbType.NVarChar,";
case "date": return "SqlDbType.Date,";
case "datetime": return "SqlDbType.DateTime,";
case "char": return "SqlDbType.Char,";
case "decimal": return "SqlDbType.Decimal,";
case "float": return "SqlDbType.Float,";
case "image": return "SqlDbType.Image,";
case "money": return "SqlDbType.Money,";
case "nchar": return "SqlDbType.NChar,";
case "ntext": return "SqlDbType.NText,";
case "real": return "SqlDbType.Real,";
case "smalldatetime": return "SqlDbType.SmallDateTime,";
case "smallint": return "SqlDbType.SmallInt,";
case "smallmoney": return "SqlDbType.SmallMoney,";
case "text": return "SqlDbType.Text,";
case "timestamp": return "SqlDbType.Timestamp,";
case "udt": return "SqlDbType.Udt,";
case "uniqueidentifier": return "SqlDbType.UniqueIdentifier,";
case "varbinary": return "SqlDbType.VarBinary,";
case "varchar": return "SqlDbType.VarChar,";
case "variant": return "SqlDbType.Variant,";
case "xml": return "SqlDbType.Xml,";
default : return "";
} }
public string GetCSharpVariableType(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;
}
}
}
</script>

代码

下载

GenrerateSqlDescription_8_28.zip

小程序大智慧,sqlserver 注释提取工具的更多相关文章

  1. sqlserver 注释提取工具

    小程序大智慧,sqlserver 注释提取工具 开篇背景 我习惯在写表的创建脚本时将注释直接写在脚本里,比如 ? /*账套*/ CREATE TABLE [dbo].[AccountingBook] ...

  2. 小程序 web 端实时运行工具

    微信小程序 web 端实时运行工具 https://chemzqm.github.io/wept/

  3. [RN] React Native代码转换成微信小程序代码的转换引擎工具

    React Native代码转换成微信小程序代码的转换引擎工具 https://github.com/areslabs/alita

  4. webstorm开发微信小程序代码提醒(webstorm开发工具)

    使用了微信提供的开发工具是真心难用,卡顿厉害.中英文切写注释换相当不爽.还没办法多开窗口,相信大家也遇到过这种现象. 下边我们介绍下webstorm来开发微信小程序的一些配置: File---sett ...

  5. 【微信小程序】在微信开发工具上七牛云的图片可以看到,但是在真机上看不到的原因解决

    在开发微信小程序过程中,在微信开发者工具上,七牛云的图片都可以展示出来,但是在真机上,七牛云的图片却展示不出来,也没有报404找不到或者不能加载图片的问题, 必须保证: 1.图片是用image加载的: ...

  6. 微信小程序---账号注册与开发工具

    (1)申请帐号 进入小程序注册页 根据指引填写信息和提交相应的资料,就可以拥有自己的小程序帐号. 在这个小程序管理平台,你可以管理你的小程序的权限,查看数据报表,发布小程序等操作. 登录 小程序后台  ...

  7. 微信小程序 使用环信聊天工具

    当时做微信小程序环信的时候,没有遇到太多的问题,因为找到了一个例子,有兴趣的朋友可以把这个包下载下来看一下,写的超级的号,使用起来也特别简单,appkey需要自己配置,从环信官网https://con ...

  8. php文档注释提取工具phpdocumentor的使用

    phpDocumentor, 分为文档性注释, 和 非文档性注释; 命令为: phpdoc -h, -f, -d.... 提取/ 生成 程序的注释文档, 实际上有很多种工具, 如: doc++, do ...

  9. 微信小程序(10)--开发者工具更新以后wxss编译错误

    更新最新版微信开发者工具后,出现下面报错: 解决办法: 1.在控制台输入openVendor() ,确定Enter: 2.清除里面的wcc.exe  wcsc.exe : 3.重启开发者工具

随机推荐

  1. eclipse git 一个错误:the current branch is not configured for pull No value for key branch.xxx.merge found

    eclipse git 一个错误:the current branch is not configured for pull No value for key branch.xxx.merge fou ...

  2. 任意长度的正小数的加法(YT新人之巅峰大决战05)

    Problem Description 话说,经过了漫长的一个多月,小明已经成长了许多,所以他改了一个名字叫"大明". 这时他已经不是那个只会做100以内加法的那个"小明 ...

  3. 联想昭阳e43l笔记本配置

    驱动精灵硬件检测报告 版本:2015.3.26.1363(8.1.326.1363)========================================================== ...

  4. Palindromes&nbsp;_easy&nbsp;version

    Time Limit: 1Sec  MemoryLimit: 64 MB Submit:165  Solved: 76 [Submit][Status][WebBoard] Description & ...

  5. hadoop出现namenode running as process 18472. Stop it first.

    hadoop出现namenode running as process 18472. Stop it first.等等,类别似几个的出现. namenode running as process 32 ...

  6. Tomcat通过配置一个虚拟路径管理web工程

    关于虚拟路径.学问javaweb训练课程,如今,鉴于这种情况下老师. 当我们的项目,当在不同的文件夹项目.我们如何使用tomcat去管理web工程. 教师提出的解决方案是 使用虚拟路径方式,并按照实施 ...

  7. PHP訪问MySql数据库 0基础篇

    在站点后台,常常要与数据库打交道.本文介绍怎样使用XAMPP来管理MySql数据库及怎样用PHP来訪问MySql数据库. 一.使用XAMPP来管理MySql数据库 首先使用XAMPP打开MySql的管 ...

  8. 初步STL集装箱Vector

    vector 特点: 1.可变长的动态数组 2.使用时包括头文件 #include <vector> 3.支持随机訪问迭代器 • 依据下标随机訪问某个元素时间为常数 • 在尾部加入速度非常 ...

  9. [Linux]history 显示命令的运行时间

    显示线时间历史命令 这里的环境是centos5.8 vim ~/.bashrc 或者 ~/.bash_profile 添加 export HISTTIMEFORMAT="%F %T &quo ...

  10. hdoj 2183 奇数阶魔方(II) 【模拟】+【法】

    比赛的时候花了一个多小时,以做不做 分析:可观察:中间是(n*n+1)/2, 中间的上面是n*n,以下是1, 左边是n,右面是(n*n+1)-n,并且正对角线是最左上对到最右下端添加(+1).另外一条 ...