首页 > 代码库 > 小程序大智慧,sqlserver 注释提取工具

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

原文:小程序大智慧,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=1,CType=typeof(string),Description="业务编号")]
[DataMember(Order = 1)]
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 生成映射类(已带注释)

核心代码

 1 namespace GenrerateSqlDescription
 2 {
 3     using System;
 4     using System.Collections.Generic;
 5     using System.IO;
 6     using System.Linq;
 7     using System.Text;
 8     using System.Text.RegularExpressions;
 9 
10 
11     public class SqlNotesGenerator
12     {
13         private readonly Regex _tableReg = new Regex(@"/[\*]+([^\*]*)[\*]+/[\r\n\s]*CREATE TABLE \[dbo\].\[(\w*)\]");
14         private readonly Regex _columnsReg = new Regex(@"\[([\w]*)\][^\/]*/[\*]+([^\*]*)[\*]+/");
15         private const string TableDescriptionCrateSqlFormat = "EXEC sp_addextendedproperty ‘MS_Description‘,‘{0}‘,‘user ‘,dbo,‘table‘,[{1}];\r\n";
16         private const string ColumnDescriptionCrateSqlFormat = "EXEC sp_addextendedproperty ‘MS_Description‘,‘{0}‘,‘user‘,dbo,‘table‘,[{1}],‘column‘,‘{2}‘;\r\n";
17         private const string TableDescriptionDropSqlFormat = "EXEC sp_dropextendedproperty ‘MS_Description‘,‘user‘,dbo,‘table‘,[{0}];\r\n";
18         private const string ColumnescriptionDropSqlFormat = "EXEC sp_dropextendedproperty ‘MS_Description‘,‘user‘,dbo,‘table‘,[{0}],‘column‘,‘{1}‘;\r\n";
19         private const string CheckTableExistsSqlFormat = "IF OBJECT_ID(N‘{0}‘,N‘U‘) IS NOT NULL \r\nBEGIN \r\n";
20         private const string EndStr = "END";
21         private const string Tab = "    ";
22 
23         public bool GenDrop { get; set; }
24         public string Path { get; set; }
25 
26         private string GetDescriptionSql(string path)
27         {
28             var sb = new StringBuilder();
29             var fs = File.OpenRead(path);
30             var fileRd = new StreamReader(fs);
31             var str = fileRd.ReadToEnd();
32             var tableMatch = _tableReg.Match(str);
33             if (tableMatch.Length < 2) return string.Empty;
34 
35             var tableName = tableMatch.Groups[2].Value;
36             var tableDes = tableMatch.Groups[1].Value;
37             if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(tableDes)) return string.Empty;
38 
39             var columnStr = str.Substring(str.IndexOf("(", StringComparison.Ordinal));
40             var matches = _columnsReg.Matches(columnStr);
41 
42             sb.AppendFormat(CheckTableExistsSqlFormat, tableName);
43 
44             sb.Append(Tab);
45             if (GenDrop)
46                 sb.AppendFormat(TableDescriptionDropSqlFormat, tableName);
47             else
48                 sb.AppendFormat(TableDescriptionCrateSqlFormat, tableDes, tableName);
49             foreach (Match match in matches)
50             {
51                 var columnName = match.Groups[1].Value;
52                 var description = match.Groups[2].Value;
53                 if (string.IsNullOrEmpty(columnName) || string.IsNullOrEmpty(description))
54                     continue;
55                 sb.Append(Tab);
56                 if (GenDrop)
57                     sb.AppendFormat(ColumnescriptionDropSqlFormat, tableName, columnName);
58                 else
59                     sb.AppendFormat(ColumnDescriptionCrateSqlFormat, description, tableName, columnName);
60             }
61 
62             sb.AppendLine(EndStr);
63             return sb.ToString();
64         }
65 
66 
67         public string GetGenerateSql()
68         {
69             Func<string, List<string>> getFolderSqlNotes = null;
70             getFolderSqlNotes = path =>
71             {
72                 var listAll = new List<string>();
73                 var files = Directory.GetFiles(path);
74                 var dirs = Directory.GetDirectories(path);
75                 foreach (string t in dirs)
76                 {
77                     listAll.AddRange(getFolderSqlNotes(t)); ;
78                 }
79                 var listStr = files.Where(m => m.EndsWith(".sql")).Select(GetDescriptionSql).ToList();
80                 listAll.AddRange(listStr);
81                 return listAll;
82             };
83             var list = getFolderSqlNotes(Path);
84             return string.Join("\r\n", list);
85         }
86 
87     }
88 
89 }
SqlNotesGenerator

CodeSimith模版

  1 <%-- 
  2 Name:
  3 Author: 
  4 Description: 
  5 --%>
  6 <%@ CodeTemplate Language="C#" TargetLanguage="C#" Src=http://www.mamicode.com/"" Inherits="OutputFileCodeTemplate" Debug="False" Description="Template description here." %>
  7 <%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema"  Category="Context"  Optional="True" Description="the table name"   %>
  8 <%@ Assembly Name="SchemaExplorer" %>
  9 <%@ Import Namespace="SchemaExplorer" %>
 10 <%@ Assembly Name="CodeSmith.BaseTemplates" %>
 11 <%@ Import Namespace="CodeSmith.BaseTemplates" %>
 12 <%@ Assembly Name="CodeSmith.CustomProperties" %>
 13 <%@ Import Namespace="CodeSmith.CustomProperties" %>
 14 //------------------------------------------------------------------------------
 15 // <auto-generated>
 16 //     This code generated by the tool, do not propose to amend
 17 //       Generation time:<%=DateTime.Now%>
 18 // </auto-generated>
 19 //------------------------------------------------------------------------------
 20 using System;
 21 using System.Data;
 22 using System.Runtime.Serialization;
 23 using XDbFramework;
 24 using BPM_M01.DataAccessLayer.Base;
 25 using System.Xml.Serialization;
 26 using System.Diagnostics;
 27 using System.CodeDom.Compiler;
 28 
 29 namespace <%=Namespace%>
 30 {
 31     [Table(TableName = <%=this.SourceTable.Name%>.Table.Name ,Descripton = "<%=this.SourceTable.Description%>",GenreratePK = <%=GenreratePK.ToString().ToLower()%>)]
 32       [GeneratedCodeAttribute("System.Xml", "2.0.50727.4927")]
 33     [DebuggerStepThroughAttribute()]
 34     public partial class <%=this.SourceTable.Name%> : DtoBase
 35     {
 36         public static class Table
 37         {
 38             public const string Name = "<%=this.SourceTable.Name%>";
 39         }
 40         public static class Columns
 41         {
 42         <%for(int i=0; i<this.SourceTable.Columns.Count;i++)
 43         {
 44             string colName=this.SourceTable.Columns[i].Name;%>
 45             public const string <%=colName%> = "<%=colName%>";
 46             public const string <%=colName%>FullName =Table.Name + "." + "<%=colName%>";            
 47         <%}%>
 48         }
 49         
 50     <%for(int i=0; i<this.SourceTable.Columns.Count;i++)
 51     {                   
 52         int namelength=this.SourceTable.Columns[i].Name.Length;
 53         string colName=this.SourceTable.Columns[i].Name;
 54         if(colName == "ID" || colName == "IDNO") continue;
 55         string titleCaseColumName = colName.Substring(0,1).ToUpper() + colName.Substring(1,colName.Length-1);
 56         %>
 57         
 58         /// <summary>
 59         /// <%=this.SourceTable.Columns[i].Description%>
 60         /// </summary>
 61         <%if(this.SourceTable.Columns[i].IsPrimaryKeyMember){%>
 62         [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%>")]
 63         <%}%>
 64         <%else{%>
 65         <%if(this.SourceTable.Columns[i].IsForeignKeyMember)%>
 66         <%// 
 67         foreach(TableKeySchema tks in SourceTable.ForeignKeys)
 68         {
 69         // 
 70             
 71             foreach(MemberColumnSchema mcs in tks.ForeignKeyMemberColumns)
 72             {
 73                 
 74                 // 
 75                 if(mcs.Name == SourceTable.Columns[i].Name)
 76                 {
 77                     TableSchema ts= tks.PrimaryKeyTable;%>
 78         [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%>")]
 79                    <% break;
 80                 }
 81             }
 82         }
 83         %>
 84         <%else{%>
 85         [Column(ColumnName=Columns.<%=colName%>,FullName=Columns.<%=colName%>FullName,Index=<%=i%>,CType=typeof(<%=GetCSharpVariableType(this.SourceTable.Columns[i])%>),Description="<%=this.SourceTable.Columns[i].Description%>")]
 86         <%}%>
 87         <%}%>
 88         [DataMember(Order = <%=i%>)]
 89         public virtual <%=GetCSharpVariableType(this.SourceTable.Columns[i])%> <%=titleCaseColumName%>{get;set;}
 90     
 91     <%}%>        
 92     }    
 93 }
 94 
 95 <script runat="template">
 96 // Override the OutputFile property and assign our specific settings to it.
 97 [FileDialog(FileDialogType.Save, Title="Select Output File", Filter="C# Files (*.cs)|*.cs", DefaultExtension=".cs")]
 98 public override string OutputFile
 99 {
100     get {return base.OutputFile;}
101     set {base.OutputFile = value;}
102 }
103 private string _Namespace;
104 public string Namespace
105 {
106     get{return _Namespace;}    
107     set{_Namespace = value;}
108 }
109 
110 public bool GenreratePK{get;set;}
111 </script>
112 <script runat="template">
113 public string GetSqlDBType(string type)
114 {
115     switch(type)
116     {
117         case "int": return "SqlDbType.Int,";
118         case "bit": return "SqlDbType.Bit,";
119         case "bigint": return "SqlDbType.BigInt,";
120         case "tinyint": return "SqlDbType.TinyInt,";
121         case "nvarchar": return "SqlDbType.NVarChar,";
122         case "date": return "SqlDbType.Date,";
123         case "datetime": return "SqlDbType.DateTime,";
124         case "char": return "SqlDbType.Char,";
125         case "decimal": return "SqlDbType.Decimal,";
126         case "float": return "SqlDbType.Float,";
127         case "image": return "SqlDbType.Image,";
128         case "money": return "SqlDbType.Money,";
129         case "nchar": return "SqlDbType.NChar,";
130         case "ntext": return "SqlDbType.NText,";
131         case "real": return "SqlDbType.Real,";
132         case "smalldatetime": return "SqlDbType.SmallDateTime,";
133         case "smallint": return "SqlDbType.SmallInt,";
134         case "smallmoney": return "SqlDbType.SmallMoney,";
135         case "text": return "SqlDbType.Text,";
136         case "timestamp": return "SqlDbType.Timestamp,";
137         case "udt": return "SqlDbType.Udt,";
138         case "uniqueidentifier": return "SqlDbType.UniqueIdentifier,";
139         case "varbinary": return "SqlDbType.VarBinary,";
140         case "varchar": return "SqlDbType.VarChar,";
141         case "variant": return "SqlDbType.Variant,";
142         case "xml": return "SqlDbType.Xml,";
143         default : return "";
144     }
145     
146 }
147 public string GetCSharpVariableType(ColumnSchema column)
148 {
149     switch (column.DataType)
150     {
151         case DbType.AnsiString: return "string";
152         case DbType.AnsiStringFixedLength: return "string";
153         case DbType.Binary: return "byte[]";
154         case DbType.Boolean: return "bool?";
155         case DbType.Byte: return "byte";
156         case DbType.Currency: return "decimal?";
157         case DbType.Date: return "DateTime?";
158         case DbType.DateTime: return "DateTime?";
159         case DbType.Decimal: return "decimal?";
160         case DbType.Double: return "double?";
161         case DbType.Guid: return "Guid";
162         case DbType.Int16: return "short?";
163         case DbType.Int32: return "int?";
164         case DbType.Int64: return "long?";
165         case DbType.Object: return "object";
166         case DbType.SByte: return "sbyte";
167         case DbType.Single: return "float?";
168         case DbType.String: return "string";
169         case DbType.StringFixedLength: return "string";
170         case DbType.Time: return "TimeSpan";
171         case DbType.UInt16: return "ushort?";
172         case DbType.UInt32: return "uint?";
173         case DbType.UInt64: return "ulong?";
174         case DbType.VarNumeric: return "decimal?";
175         default:
176         {
177             return "__UNKNOWN__" + column.NativeType;
178         }
179     }
180 }
181 </script>
代码

下载

GenrerateSqlDescription_8_28.zip