Visual Studio Package 插件开发之自动生成实体工具

前言

  这一篇是VS插件基于Visual Studio SDK扩展开发的,可能有些朋友看到【生成实体】心里可能会暗想,T4模板都可以做了、动软不是已经做了么、不就是读库保存文件到指定路径么……

  我希望做的效果是:

  1.工具集成到vs上

  2.动作完成后体现到项目(添加、删除项目项)

  3.使用简单、轻量、灵活(配置化)

  4.不依赖ORM(前两点有点像EF的DBFirst吧?)

  文章最后会给上源码地址。

   下面是效果图:

处理流程

  

  以上是完整处理流程,我打算选择部分流程来讲。如果有对Visual Studio Package开发还没一个认识,可以看我之前写的一篇Visual Studio Package 插件开发

按钮的位置

  

  从上图看见,按钮是在选中项目右键弹出的菜单栏里。

  打开vsct文件,修改Group的Parent节点,修改对应的guid和id。

  之前那边文章有提到在文件:您的vs安装目录\VisualStudio2013\VSSDK\VisualStudioIntegration\Common\Inc\vsshlids.h 可以找到需要修改的名称,但是右键是没有在文件里定义,因此我们需要另外换一种方法。

  1、打开注册表编辑器(打开运行窗口,输入regedit),

  2、路径[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0\General],

  3、右击-新建-DWORD(32-位)值(D),其命名为EnableVSIPLogging

  4、并将其值改为1。重启VS,打开项目

  5、按下Ctrl+Shift,对项目点击右键,就会弹出窗口(如下图)

  

  Guid和CmdID的值就是我们需要的,在vsct文件Symbols节点添加GuidSymbol项,value上图的{D309F791-903F-11D0-9EFC-00A0C911004F},IDSymbolvalue1026。

  最后在Group的Parent节点的属性guidid改为与上面对应下面代码为例子。

<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <Commands package="guidAutoBuildEntityPkg">

    <Groups>
      <Group guid="guidAutoBuildEntityCmdSet" id="MyMenuGroup" priority="0x0600">
        <Parent guid="guidCodeWindowRightClickCmdSet" id="CodeWindowRightClickMenu"/>
      </Group>
    </Groups>

  </Commands>

  <Symbols>
    <GuidSymbol name="guidAutoBuildEntityPkg" value="{c095f8f8-3f87-4eac-8dc0-44939a85b2f2}" />

    <GuidSymbol name="guidCodeWindowRightClickCmdSet" value="{D309F791-903F-11D0-9EFC-00A0C911004F}">
      <IDSymbol name="CodeWindowRightClickMenu" value="1026" />
    </GuidSymbol>

  </Symbols>

</CommandTable>

读取选中项目信息

  重点是DTE 接口的使用,MSDN的描述是:DTE 接口Visual Studio 自动化对象模型中的顶级对象。强大到当前开发环境中任何属性可以拿到例如:当前打开的文档集合,解决方案下的项目信息……剩下自己看,传送门

   下面是代码示例:

       var dte = (DTE)GetService(typeof(SDTE));

         /// <summary>
        /// 获取选中项目的信息
        /// </summary>
        /// <param name="dte"></param>
        /// <returns></returns>
        public static SelectedProject GetSelectedProjectInfo(this DTE dte)
        {
            var selectedItems = dte.SelectedItems;

            var projectName = (from SelectedItem item in selectedItems select item.Name).ToList();

            if (!selectedItems.MultiSelect && selectedItems.Count == 1)
            {
                var selectProject = selectedItems.Item(projectName.First());

                var projectFileList = (from ProjectItem projectItem in selectProject.Project.ProjectItems
                                       where projectItem.Name.EndsWith(".cs")
                                       select Path.GetFileNameWithoutExtension(projectItem.Name)).ToList();

                return new SelectedProject(selectProject.Project.FullName, selectProject.Project, projectFileList);
            }

            return null;
        }        

读取实体配置信息

  配置存放两点信息:数据库连接、类文件模版,同时我们约定存放在项目根目录下。如下图

  

  那么,剩下就是XML的基本获取处理了。__entity.xml的模版在源码里,可自行拷贝去需要使用的项目,以下是代码示例:

private void AutoBuildEntityEvent(object sender, EventArgs e)
        {
            var autoBuildEntityContent = new AutoBuildEntityContent ();

            //读取选中项目下的配置信息
            var entityXmlModel = new EntityXml(autoBuildEntityContent.SelectedProject.EntityXmlPath);
            entityXmlModel.Load();
            autoBuildEntityContent.EntityXml = entityXmlModel;

            new MainForm(autoBuildEntityContent).ShowDialog();
        }

    public class EntityXml
    {
        private readonly string _path;

        public EntityXml(string path)
        {
            _path = path;
        }

        public string ConnString { get; private set; }

        public string EntityTemplate { get; private set; }

        /// <summary>
        /// 读取_entity.xml
        /// </summary>
        /// <returns></returns>
        public EntityXml Load()
        {
            var xml = new XmlDocument();
            xml.Load(_path);

            var autoEntityNode = xml.SelectSingleNode("AutoEntity");
            if (autoEntityNode != null)
            {
                var connStringNode = autoEntityNode.SelectSingleNode("ConnString");
                if (connStringNode != null)
                {
                    ConnString = connStringNode.InnerText;
                }
                var templatesNodes = autoEntityNode.SelectSingleNode("Template");
                if (templatesNodes != null)
                {
                    EntityTemplate = templatesNodes.InnerText;
                }
            }

            return this;
        }
    }

读取物理表

  查询当前数据库的表集合,传给窗体做列表展示,直接上代码:

/// <summary>
    /// 物理表
    /// </summary>
    public class DbTable
    {
        public string TableName { get; private set; }

        public List<TableColumn> Columns { get; set; }

        private readonly string _conn;

        public DbTable(string conn)
        {
            _conn = conn;
        }

        public DbTable(string tableName, List<TableColumn> columns)
        {
            TableName = tableName;
            Columns = columns;
        }

        public List<string> QueryTablesName()
        {
            var result = SqlHelper.Query(_conn, @"SELECT  name FROM    sysobjects WHERE  xtype IN ( ‘u‘,‘v‘ ); ");

            return (from DataRow row in result.Rows select row[0].ToString()).ToList();
        }

        public List<DbTable> GetTables(List<string> tablesName)
        {
            if (!tablesName.Any())
                return new List<DbTable>();

            var t = new TableColumn(_conn);

            var columns = t.QueryColumn(tablesName);

            return columns.GroupBy(a => a.TableName).Select(a => new DbTable(a.Key, a.ToList())).ToList();
        }

    }

读取表结构

  选择响应的表后,查询出对应的表结构,一般实体的所需要的信息有:列名、列备注、类型、长度、是否主键、是否自增长、是否可空,继续上代码:

/// <summary>
    /// 物理表的列信息
    /// </summary>
    public class TableColumn
    {
        private readonly string _connStr;
        public TableColumn()
        {

        }
        public TableColumn(string connStr)
        {
            _connStr = connStr;
        }

        public string TableName { get; private set; }

        public string Name { get; private set; }

        public string Remark { get; private set; }

        public string Type { get; private set; }

        public int Length { get; private set; }

        public bool IsIdentity { get; private set; }

        public bool IsKey { get; private set; }

        public bool IsNullable { get; private set; }

        public string CSharpType
        {
            get
            {
                return SqlHelper.MapCsharpType(Type, IsNullable);
            }
        }

        /// <summary>
        /// 查询列信息
        /// </summary>
        /// <param name="tablesName"></param>
        /// <returns></returns>
        public List<TableColumn> QueryColumn(List<string> tablesName)
        {
            #region 表结构

            var paramKey = string.Join(",", tablesName.Select((a, index) => "@p" + index));
            var paramVal = tablesName.Select((a, index) => new SqlParameter("@p" + index, a)).ToArray();
            var sql = string.Format(@"SELECT  obj.name AS tablename ,
        col.name ,
        ISNULL(ep.[value], ‘‘) remark ,
        t.name AS type ,
        col.length ,
        COLUMNPROPERTY(col.id, col.name, ‘IsIdentity‘) AS isidentity ,
        CASE WHEN EXISTS ( SELECT   1
                           FROM     dbo.sysindexes si
                                    INNER JOIN dbo.sysindexkeys sik ON si.id = sik.id
                                                              AND si.indid = sik.indid
                                    INNER JOIN dbo.syscolumns sc ON sc.id = sik.id
                                                              AND sc.colid = sik.colid
                                    INNER JOIN dbo.sysobjects so ON so.name = si.name
                                                              AND so.xtype = ‘PK‘
                           WHERE    sc.id = col.id
                                    AND sc.colid = col.colid ) THEN 1
             ELSE 0
        END AS iskey ,
        col.isnullable
FROM    dbo.syscolumns col
        LEFT  JOIN dbo.systypes t ON col.xtype = t.xusertype
        INNER JOIN dbo.sysobjects obj ON col.id = obj.id
                                         AND obj.xtype IN ( ‘U‘, ‘v‘ )
                                         AND obj.status >= 0
        LEFT  JOIN dbo.syscomments comm ON col.cdefault = comm.id
        LEFT  JOIN sys.extended_properties ep ON col.id = ep.major_id
                                                 AND col.colid = ep.minor_id
                                                 AND ep.name = ‘MS_Description‘
        LEFT  JOIN sys.extended_properties epTwo ON obj.id = epTwo.major_id
                                                    AND epTwo.minor_id = 0
                                                    AND epTwo.name = ‘MS_Description‘
WHERE   obj.name IN ({0});", paramKey);

            #endregion

            var result = SqlHelper.Query(_connStr, sql, paramVal);

            return (from DataRow row in result.Rows
                    select new TableColumn
                    {
                        IsIdentity = Convert.ToBoolean(row["isidentity"]),
                        IsKey = Convert.ToBoolean(row["iskey"]),
                        IsNullable = Convert.ToBoolean(row["isnullable"]),
                        Length = Convert.ToInt32(row["length"]),
                        Name = row["name"].ToString(),
                        Remark = row["remark"].ToString(),
                        TableName = row["tablename"].ToString(),
                        Type = row["type"].ToString()
                    }).ToList();
        }
    }

根据模板生成代码

  开始我是尝试用T4的,发现不方便,繁杂的声明。因此我选择了nVelocity,这里不做太多介绍,附上相关文章学习,传送门

// <summary>
        /// 初始化模板引擎
        /// </summary>
        public static string ProcessTemplate(string template, Dictionary<string, object> param)
        {
            var templateEngine = new VelocityEngine();
            templateEngine.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file");

            templateEngine.SetProperty(RuntimeConstants.INPUT_ENCODING, "utf-8");
            templateEngine.SetProperty(RuntimeConstants.OUTPUT_ENCODING, "utf-8");

            templateEngine.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, AppDomain.CurrentDomain.BaseDirectory);

            var context = new VelocityContext();
            foreach (var item in param)
            {
                context.Put(item.Key, item.Value);
            }

            templateEngine.Init();

            var writer = new StringWriter();
            templateEngine.Evaluate(context, writer, "mystring", template);

            return writer.GetStringBuilder().ToString();
        }

  之前已经拿到的文件模版,通过上面的方法输出类文本,保存到选中项目的根目录下。

 public static class FilesHelper
    {
        public static string Write(string directory, string fileName, string content)
        {
            var path = Path.Combine(directory, fileName + ".cs");
            using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                byte[] byteFile = Encoding.UTF8.GetBytes(content);
                fs.Write(byteFile, 0, byteFile.Length);
            }
            return path;
        }
    }

操作项目

  终于到了最后一步了,部分人以为保存了文件后就完事了,最后通过包含文件就完事了。我们还是有点追求的,既然做成了插件就要更加的方便化。

  通过之前[读取选中项目信息]步骤拿到的EnvDTE.Project ProjectDte,使用以下扩展方法进行添加、删除项目项

 /// <summary>
        /// 添加项目项
        /// </summary>
        /// <param name="projectDte"></param>
        /// <param name="files"></param>
        public static void AddFilesToProject(this Project projectDte, List<string> files)
        {
            foreach (string file in files)
            {
                projectDte.ProjectItems.AddFromFile(file);
            }

            if (files.Any())
                projectDte.Save();
        }

        /// <summary>
        /// 排除项目项
        /// </summary>
        /// <param name="projectDte"></param>
        /// <param name="files"></param>
        public static void RemoveFilesFromProject(this Project projectDte, List<string> files)
        {
            foreach (string file in files)
            {
                projectDte.ProjectItems.Item(Path.GetFileName(file)).Remove();
            }

            if (files.Any())
                projectDte.Save();
        }

附加

  部分同学可能想调试的时候会出现:无法直接启动“类库输出类型”项目,可以在项目属性-调试配置:

  1.启动配置外部程序:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe

  2.命令行参数:/rootsuffix Exp

  

  估计有同学会制作自己的图标,另外附上两条icon制作的网站:

  http://iconfont.cn/search/index

  http://www.easyicon.net/covert/

结尾

  整篇文章的技术难点并不多,但是因为插件开发的资料相对较少,80%的时间花去找接口文档、找资料。

  此工具的原型是公司架构师的,公司所有开发都在用,但是他把源码丢了………………好奇心使我重新实现了一份,当然了,说不定哪天带团队的时候会用上。

  最后双手奉上源码,并不是什么牛逼的东西,希望可以帮助需要的同学。https://github.com/SkyChenSky/AutoBuildEntity

  如果本篇文章对您有帮助,可以点击左下角的推荐,这是给我最大的鼓励,如果有什么建议和优化,可以在下面评论提出,谢谢。

时间: 2024-10-10 12:34:45

Visual Studio Package 插件开发之自动生成实体工具的相关文章

七色花基本权限系统(5)- 实体配置的使用和利用T4自动生成实体配置

在前面的章节里,用户表的结构非常简单,没有控制如何映射到数据库.通常,需要对字段的长度.是否可为空甚至特定数据类型进行设置,因为EntityFramework的默认映射规则相对而言比较简单和通用.在这篇日志里,将演示如何对数据实体进行映射配置,并利用T4模板自动创建映射配置类文件. 配置方式 EntityFramework的实体映射配置有2种. 第一种是直接在实体类中以特性的方式进行控制,这些特性有部分是EF实现的,也有部分是非EF实现的.也就是说,在数据实体层不引用EF的情况下,只能使用不全的

使用MyBatis Generator自动生成实体、mapper和dao层

通过MyBatis Generator可以自动生成实体.mapper和dao层,记录一下怎么用的. 主要步骤: 关于mybatis从数据库反向生成实体.DAO.mapper: 参考文章:http://www.cnblogs.com/wangkeai/p/6934683.html第一种方式:main方法运行(推荐) 1.在pom.xml中加入插件依赖: 2.写mbgConfiguration.xml文件,jdbc.properties文件 3.写/SSM/src/main/java/main/Ge

自己动手让Visual Studio的Win32向导支持生成对话框程序

Visual Studio的MFC向导可以生成基于对话框的程序,而win32向导却不支持,是不是很不给力呢?虽然VAX可以直接插入对话框的代码段,但还是没有直接生成来得爽,下面就动手来改造吧. 本文所做的修改仅在自己的VS里测试通过,大家修改之前请备份原文件. 那我们需要改什么呢?看图吧. 首先,找到"VS安装目录\VC\VCWizards\AppWiz\Generic\Application\",如我电脑上的目录就是 "D:\Program Files\Microsoft

IDEA——mybatis-generator插件自动生成实体代码(Maven)

利用MyBatis生成器自动生成实体类.DAO接口和Mapping映射文件.  mysql-connector-java-5.1.6-bin.jar mysql驱动包  mybatis-generator-core-1.3.5.jar 自动生成器包 maven 配置mybatis-generator插件 一.pom.xml 两处配置 (1) (2) 二.创建 generatorConfig.xml 配置如下: 1 <?xml version="1.0" encoding=&quo

SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍

原文:SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍 由于开发SSMS2008插件是通过VS2008进行的,有必要先介绍一下VS2008的插件开发过程. 这次的目的是在VS2008的工具菜单中增加一个菜单项"Test Menu",该菜单项实现显示"Hello World"的功能. 1.打开"新建项目"对话框.在"项目类型"中选择"其他项目类型"--&g

用Visual Studio 2010编写Data Url生成工具C#版

声明:本文系本人依照真实经历原创,未经许可,谢绝转载. 此文百度经验版本:如何用Visual Studio 2010打造Data Url生成工具 源码下载:用Visual Studio 2010编写Data Url生成工具C#源码 什么是Data Url呢?Data URI scheme是在RFC2397中定义的,目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入. 例如: <img src="data:image/png;base64,iVBORw0KGgoAAAANSUh

Mybatis自动生成实体类、dao接口和mapping映射文件

由于Mybatis是一种半自动的ORM框架,它的工作主要是配置mapping映射文件,为了减少手动书写映射文件,可以利用mybatis生成器,自动生成实体类.dao接口以及它的映射文件,然后直接拷贝到工程中稍微修改就可以直接使用了. 生成器目录如下: 首先进入lib文件夹中,该目录如下: (图上文件下载地址:http://download.csdn.net/detail/qiwei31229/9790909) 主要修改generatorConfig.xml <?xml version="1

C#集合篇,在业务背景下(***产品升级管理):依赖注入,变量声明,三元表达式,常用字符串相关操作方法,ADO.NET,EF机制,T4模板自动生成实体类,ref变量巧用,属性实际运用,唯一性验证

QQ:1187362408 欢迎技术交流和学习 关于系统产品升级报告管理,业务需求: TODO: 1,升级报告管理:依据各县区制定升级报告(关联sAreaCode,给每个地区观看具体升级报告信息) 2,运用的技术:依赖注入,变量声明,三元表达式,常用字符串相关操作方法,ADO.NET,EF机制,T4模板自动生成实体类,ref变量与可null变量巧用,属性实际运用,唯一性验证,url传递中文编码和解码问题 讲解篇:1,服务端aspx,2,服务端后台返回数据(这里采用服务器端程序:aspx.cs)

Visual Studio 2010添加数据源时没有实体数据模型Entity Data Model选项

Visual Studio 2010添加数据源时没有实体数据模型Entity Data Model选项 今天在用VS2010创建控制台应用程序,添加数据源的时候,没有"实体数据模型"选项.在网上搜索了下,很多人都遇到了这个问题.我最后找到了解决方案. 在安装文件夹中找到WCU\EFTools文件夹,如果直接运行msi文件会报错:To install this product please run Install.exe.将里边cab和msi文件拷贝出来到E盘(便于操作),并创建Log.