• 使用Visual Studio宏来自动生成代码 [ Visual Studio | 宏 | 自动生成代码 ]


    前言

          宏的定义:是组合到一起形成一个命令以自动完成某项任务的一系列命令和指令。(MSDN)

          在使用Visual Studio宏实现JS折叠功能的时候就想过用它来实现代码自动生成,有了前面的基础,实现起来就不那么困难了,本文将实现根据表名自动生成相关字段属性的Model代码。

    正文

          一、预备

                1.1      关于宏的新建以及简单用法,请参见这里

                1.2      环境 Microsoft Visual Studio 2008、Microsoft SQL Server 2000

          二、目的

                根据类名(类名和表名须一致)自动生成列名、字段说明、属性(Properties)、构造函数。

          三、实现步骤

                3.1      准备测试用表结构 

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[User]'and OBJECTPROPERTY(id, N'IsUserTable'= 1)
    drop table [dbo].[User]
    GO

    CREATE TABLE [dbo].[User] (
        
    [UniqueID] [int] IDENTITY (11NOT NULL ,
        
    [Username] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
        
    [Password] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
        
    [Name] [char] (10) COLLATE Chinese_PRC_CI_AS NULL ,
        
    [UserPermission_Id] [int] NULL 
    ON [PRIMARY]
    GO

    ALTER TABLE [dbo].[User] ADD 
        
    CONSTRAINT [PK_Users] PRIMARY KEY  CLUSTERED 
        (
            
    [UniqueID]
        )  
    ON [PRIMARY] 
    GO

    exec sp_addextendedproperty N'MS_Description', N'姓名', N'user', N'dbo', N'table', N'User', N'column', N'Name'
    GO
    exec sp_addextendedproperty N'MS_Description', N'密码', N'user', N'dbo', N'table', N'User', N'column', N'Password'
    GO
    exec sp_addextendedproperty N'MS_Description', N'用户名', N'user', N'dbo', N'table', N'User', N'column', N'Username'
    GO
    exec sp_addextendedproperty N'MS_Description', N'用户权限', N'user', N'dbo', N'table', N'User', N'column', N'UserPermission_Id'

    GO

                3.2      准备获取表结构帮助类(C#)

                      3.2.1      新建项目 -> 类库,项目名称:SqlSchemaProvider

                      2.2.2      数据传输类ColumnInfo.cs 

    using System;
    using System.Collections.Generic;
    using System.Text;

    /// <summary>
    /// 字段信息
    /// </summary>
    public sealed class ColumnInfo
    {
        
    #region Member Variable

        
    private string name;
        
    private string desc;
        
    private string type;

        
    #endregion

        
    #region Constructor

        
    public ColumnInfo(string name, string type, string desc)
        {
            
    this.name = name;
            
    this.desc = desc;
            
    this.type = type;
        }

        
    #endregion

        
    #region Properties

        
    /// <summary>
        
    /// 列名
        
    /// </summary>
        public string Name
        {
            
    get { return name; }
            
    set { name = value; }
        }

        
    /// <summary>
        
    /// 列说明
        
    /// </summary>
        public string Description
        {
            
    get { return desc; }
            
    set { desc = value; }
        }

        
    /// <summary>
        
    /// 数据类型(已经转换为C#)
        
    /// </summary>
        public string Type
        {
            
    get { return type; }
            
    set { type = value; }
        }

        
    #endregion

    }

                      2.2.3      元数据获取帮助类SqlSchemaProvider.cs ,注意这里使用了SqlHelper.cs!

    //==============================================================================
    //
    // 作 者:农民伯伯
    // 邮 箱:over140@gmail.com
    // 博 客:http://over140.cnblogs.com/
    // 时 间:2009-6-24
    // 描 述:获取SQL SERVER 元数据
    //
    //==============================================================================


    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data;
    using System.Data.SqlClient;

    public sealed class SqlSchemaProvider
    {

        
    #region GetTableColumns

        
    public ColumnInfo[] GetTableColumns(string connectstring, string tableName)
        {
            List
    <ColumnInfo> result = new List<ColumnInfo>();

            SqlConnectionStringBuilder scsb 
    = new SqlConnectionStringBuilder(connectstring);
            
    using (SqlDataReader reader = SqlHelper.ExecuteReader(scsb.ConnectionString, CommandType.Text, SQL2000_GetTableColumns,
                
    new SqlParameter("@DatabaseName", scsb.InitialCatalog),
                
    new SqlParameter("@SchemaName""dbo"),
                
    new SqlParameter("@TableName", tableName)))
            {
                
    while (reader.Read())
                {
                    result.Add(
    new ColumnInfo(reader.GetString(0), GetCSharpType(reader.GetString(1)), reader.GetString(17)));
                }
            }
            
    return result.ToArray();
        }

        
    #region Type Maps

        
    private string GetCSharpType(string type)
        {
            
    if (string.IsNullOrEmpty(type))
                
    return "string";

            
    string reval = string.Empty;
            
    switch (type.ToLower())
            {
                
    case "varchar":
                
    case "nchar":
                
    case "ntext":
                
    case "text":
                
    case "char":
                
    case "nvarchar":
                    reval 
    = "string";
                    
    break;
                
    case "int":
                    reval 
    = "int";
                    
    break;
                
    case "smallint":
                    reval 
    = "Int16";
                    
    break;
                
    case "bigint":
                    reval 
    = "Int64";
                    
    break;
                
    case "float":
                    reval 
    = "double";
                    
    break;
                
    case "bit":
                    reval 
    = "bool";
                    
    break;
                
    case "decimal":
                
    case "smallmoney":
                
    case "money":
                
    case "numeric":
                    reval 
    = "decimal";
                    
    break;
                
    case "binary":
                    reval 
    = "System.Byte[]";
                    
    break;
                
    case "real":
                    reval 
    = "System.Single";
                    
    break;
                
    case "datetime":
                
    case "smalldatetime":
                
    case "timestamp":
                    reval 
    = "System.DateTime";
                    
    break;
                
    case "tinyint":
                    reval 
    = "System.Byte";
                    
    break;
                
    case "uniqueidentifier":
                    reval 
    = "System.Guid";
                    
    break;
                
    case "image":
                
    case "varbinary":
                    reval 
    = "System.Byte[]";
                    
    break;
                
    case "Variant":
                    reval 
    = "Object";
                    
    break;
                
                
    default:
                    reval 
    = "string";
                    
    break;
            }
            
    return reval;
        } 

        
    #endregion

        
    #endregion

        
    #region SQL Templates

        
    #region GetTableColumns

        
    private const string SQL2000_GetTableColumns = @"
                  SELECT
                    clmns.[name] AS [Name],
                    usrt.[name] AS [DataType],
                    ISNULL(baset.[name], N'') AS [SystemType],
                    CAST(CASE WHEN baset.[name] IN (N'char', N'varchar', N'binary', N'varbinary', N'nchar', N'nvarchar') THEN clmns.prec ELSE clmns.length END AS INT) AS [Length],
                    CAST(clmns.xprec AS TINYINT) AS [NumericPrecision],
                    CAST(clmns.xscale AS INT) AS [NumericScale],
                    CASE CAST(clmns.isnullable AS BIT) WHEN 1 THEN 'YES' ELSE 'NO' END AS [Nullable],
                    defaults.text AS [DefaultValue],
                    CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsIdentity') AS INT) AS [Identity],
                    CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsRowGuidCol') AS INT) AS IsRowGuid,
                    CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsComputed') AS INT) AS IsComputed,
                    CAST(COLUMNPROPERTY(clmns.id, clmns.[name], N'IsDeterministic') AS INT) AS IsDeterministic,
                    CAST(CASE COLUMNPROPERTY(clmns.id, clmns.[name], N'IsIdentity') WHEN 1 THEN IDENT_SEED(QUOTENAME(stbl.[name]) + '.' + QUOTENAME(tbl.[name])) ELSE 0 END AS NVARCHAR(40)) AS [IdentitySeed],
                    CAST(CASE COLUMNPROPERTY(clmns.id, clmns.[name], N'IsIdentity') WHEN 1 THEN IDENT_INCR(QUOTENAME(stbl.[name]) + '.' + QUOTENAME(tbl.[name])) ELSE 0 END AS NVARCHAR(40)) AS [IdentityIncrement],
                    cdef.[text] AS ComputedDefinition,
                    clmns.[collation] AS Collation,
                    CAST(clmns.colid AS int) AS ObjectId,
            isnull(prop.value, '') AS ColumnDesc
                  FROM
                    dbo.sysobjects AS tbl
                    INNER JOIN dbo.sysusers AS stbl ON stbl.[uid] = tbl.[uid]
                    INNER JOIN dbo.syscolumns AS clmns ON clmns.id=tbl.id
                    LEFT JOIN dbo.systypes AS usrt ON usrt.xusertype = clmns.xusertype
                    LEFT JOIN dbo.sysusers AS sclmns ON sclmns.uid = usrt.uid
                    LEFT JOIN dbo.systypes AS baset ON baset.xusertype = clmns.xtype and baset.xusertype = baset.xtype
                    LEFT JOIN dbo.syscomments AS defaults ON defaults.id = clmns.cdefault
                    LEFT JOIN dbo.syscomments AS cdef ON cdef.id = clmns.id AND cdef.number = clmns.colid
            LEFT OUTER JOIN sysproperties prop ON clmns.id = prop.id AND clmns.colid = prop.smallid 
                  WHERE
                    (tbl.[type] = 'U' OR tbl.[type] = 'S') 
                    AND stbl.[name] = 'dbo'
                    AND tbl.[name] =
    @TableName
                  ORDER BY
                      clmns.colorder
    ";

        
    #endregion

        
    #endregion

    }

                3.3      宏中引用帮助类

                      3.3.1      由于不能直接用绝对路径添加dll,所以需要将SqlSchemaProvider.dll拷贝到<安装目录>\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies

                      3.3.2      引用System.Data.dll和SqlSchemaProvider.dll

           

                3.4      编写宏代码

    Imports System
    Imports EnvDTE
    Imports EnvDTE80
    Imports EnvDTE90
    Imports System.Diagnostics
    Imports System.Collections.Generic

    '============================================================================
    '
    '
     作 者:农民伯伯
    '
     邮 箱:over140@gmail.com
    '
     博 客:http://over140.cnblogs.com/
    '
     时 间:2009-6-24
    '
     描 述:自动生成Model宏
    '
    '
    ============================================================================
    Public Module AutoModel

        
    Dim selection As EnvDTE.TextSelection

        
    Sub GenerateModel()
            
    Dim schemaProvide As SqlSchemaProvider = New SqlSchemaProvider()
            
    Const ConnStr As String = "Data Source=.;Initial Catalog=DBName;User ID=sa;Password=sa;"
            
    Dim tableName As String
            
    Dim columns() As ColumnInfo         '用于存放字段信息
            Dim line As Integer

            selection 
    = DTE.ActiveDocument.Selection
            tableName 
    = selection.Text

            
    '------------------------------------------------------验证
            If String.IsNullOrEmpty(tableName) Then
                
    MsgBox("请选择要表名!", MsgBoxStyle.OkOnly)
                
    Return
            
    End If

            
    '取得所有字段
            columns = schemaProvide.GetTableColumns(ConnStr, tableName)

            
    If columns.Length = 0 Then
                
    MsgBox("表不存在或该表没有字段!", MsgBoxStyle.OkOnly)
                
    Return
            
    End If

            
    '移动当前行位置,添加占位行
            line = selection.ActivePoint.Line + 2
            selection.GotoLine(line)
            selection.NewLine(columns.Length 
    * 2)
            selection.GotoLine(line)

            
    '------------------------------------------------------成员变量
            NewLineInsert("#region Member Variable")
            selection.NewLine(
    2)

            
    For Each column As ColumnInfo In columns

                InsertNewLine(
    String.Format("private {0} {1};", column.Type, column.Name.ToLower()))

            
    Next

            NewLineInsert(
    "#endregion")
            selection.NewLine()

            
    '------------------------------------------------------构造函数
            NewLineInsert("#region Constructor")
            selection.NewLine()

            NewLineInsert(
    String.Format("public {0}()", tableName))
            NewLineInsert(
    "{")
            NewLineInsert(
    "}")

            NewLineInsert(
    "#endregion")
            selection.NewLine()

            
    '------------------------------------------------------字段
            NewLineInsert("#region 字段名称")
            selection.NewLine(
    2)

            
    For Each column As ColumnInfo In columns

                InsertNewLine(
    "/// <summary>")
                
    If (String.IsNullOrEmpty(column.Description)) Then
                    InsertNewLine(
    "没有字段说明")
                
    Else
                    InsertNewLine(column.Description)
                
    End If
                InsertNewLine(
    "</summary>")
                selection.GotoLine(selection.AnchorPoint.Line)
                selection.SelectLine()

                InsertNewLine(
    String.Format("public const string CN_{0} = ""{0}"";", column.Name))
                selection.NewLine()

            
    Next

            InsertNewLine(
    "#endregion")

            
    '------------------------------------------------------属性(Properties)
            NewLineInsert("#region Properties")
            selection.NewLine(
    2)

            
    For Each column As ColumnInfo In columns

                InsertNewLine(
    "/// <summary>")
                
    If (String.IsNullOrEmpty(column.Description)) Then
                    InsertNewLine(
    "没有字段说明")
                
    Else
                    InsertNewLine(column.Description)
                
    End If
                InsertNewLine(
    "</summary>")
                selection.GotoLine(selection.AnchorPoint.Line)
                selection.SelectLine()

                InsertNewLine(
    String.Format("public {0} {1}", column.Type, System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(column.Name)))
                InsertNewLine(
    "{")
                InsertNewLine(
    String.Concat("get { return ", column.Name.ToLower(), "; }"))
                InsertNewLine(
    String.Concat("set { ", column.Name.ToLower(), " = value; }"))
                InsertNewLine(
    "}")
                selection.NewLine()

            
    Next

            NewLineInsert(
    "#endregion")
            selection.NewLine()

            
    '格式化文档 
            'DTE.ExecuteCommand("Edit.FormatDocument")
            DTE.ExecuteCommand("编辑.设置文档的格式")
            
    '折叠代码
            DTE.ExecuteCommand("编辑.折叠到定义")

        
    End Sub

        
    Sub NewLineInsert(ByVal code As String)
            
    '相当于在编辑器内按Enter键
            selection.NewLine()
            selection.Insert(code)
        
    End Sub

        
    Sub InsertNewLine(ByVal code As String)
            selection.Insert(code)
            selection.NewLine()
        
    End Sub

    End Module

                      代码说明:

                            a).      如果不熟悉VBA编程建议尽量将逻辑代码、访问数据库代码用C#写成类库再引用进来调用。

                            b).      注意连接数据库字符串ConnStr需要替换成自己的数据库连接字符串!

                            c).      输出代码的排版并不全是简单的一行输出一个字串就行了,需要模拟你在编辑器中输入代码的同时VS自动生成的代码,比如在一个属性(Property)上行输入"///"他就会自动给你生成注释,如果你在后面的行还输出"/// 说明"那就会出错了,格式也乱了,所以需要特别注意!

                            d).      宏最后有"DTE.ExecuteCommand("编辑.设置文档的格式")"这样的代码,这个用于直接调用工具栏里面功能,这里写英文的行,写中文的也行:)

                3.5      设置运行宏

                      3.5.1      设置宏快捷键(参照前文),这里设置为Ctrl+G、Ctrl+M

                      3.5.2      新建类User,注意类名和表名须一致

        class User
        {

        }

                       3.5.3      选中类名User,快捷键Ctrl+G、Ctrl+M运行宏,生成如下:

             

            展开部分代码

             

    结束语

          曾做过一次对日外包的项目,印象最深的是他们Excel用得非常好,用来写式样书、写测试报告,用Excel中的宏来生成数据库、生成测试数据等复杂的操作,非常强悍,至此也算小小的满足了一下羡慕的心理: )

    本博Visual Studio相关文章

          1.      让Visual Studio 也支持JS代码折叠 —— 续 [ Visual Studio | Js | ScriptOutline | SmallOutline ]

          2.  Visual Studio 让JS也支持代码折叠 [ Visual Studio | #region | #endregion ]

          3.  Visual Studio 模板 —— 自定义WebForm模板

          4.  用Visual Studio 2005/2008提取EXE文件中的资源[图片|htm|光标文件]

          5.  Microsoft Visual Studio 2005 快捷键提示

          6.  Visual Studio 在根目录下运行文件系统站点 [ Visual Studio | 文件系统 | WebDev.WebServer.EXE ]

  • 相关阅读:
    痞子衡嵌入式:浅谈i.MXRT1xxx系列MCU时钟相关功能引脚的作用
    痞子衡嵌入式:大话双核i.MXRT1170之在线联合调试双核工程的三种方法(IAR篇)
    220505 How cookie works
    [前端也需要学一点设计]_色彩篇_如何创建色盘
    怎么用 Solon 开发基于 undertow jsp tld 的项目?(新)
    Solon 1.7 重要发布,更现代感的应用开发框架
    Water 2.6.3 发布,一站式服务治理平台
    Solon 项目的单元测试插件
    Next.js 在 Serverless 中从踩坑到破茧重生
    CentOS 8迁移Rocky Linux 8手记
  • 原文地址:https://www.cnblogs.com/over140/p/1510162.html
Copyright © 2020-2023  润新知