前言

  这一篇是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改为与上面对应下面代码为例子。

PS:上面方法有点久远了,现在2017、2019可以用新的方式来查找需要的功能guid与cmdID。

在VS的【扩展与更新】搜索并安装Extensibility Tools,然后在vs【视图】-【 Enable VSIP Logging】点击并重启后,就可以用ctrl+shirt+右键点击需要查的界面,就可以弹出需要的信息,我测试过vs2017可用。

非常感谢yanusosu兄弟的贡献。

<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 == )
{
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[].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, , 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

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

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

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

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

  2. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  3. 使用T4为数据库自动生成实体类

    T4 (Text Template Transformation Toolkit) 是一个基于模板的代码生成器.使用T4你可以通过写一些ASP.NET-like模板,来生成C#, T-SQL, XML ...

  4. Mybatis自动生成实体类

    Maven自动生成实体类需要的jar包 一.pom.xml中 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...

  5. 【.NetCore学习】ASP.NET Core EF Core2.0 DB First现有数据库自动生成实体Context

    主要参考微软官方文档 https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db Microsoft .NE ...

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

    原文链接 通过MyBatis Generator可以自动生成实体.mapper和dao层,记录一下怎么用的. 主要步骤: 关于mybatis从数据库反向生成实体.DAO.mapper: 参考文章:ht ...

  7. Linq to Sql自动生成实体类重名情况的处理

    使用Linq to sql自动生成实体类时,如果要生成多个库的实体类,往往会遇到类名重名的情况,也就是表名重名,这样编译会不通过,这种情况下要在自动生成的实体类文件中(.designer.cs后缀)将 ...

  8. 在idea中,mavne项目使用mybatis-generator-maven-plugin自动生成实体了的时候,在maven插件里面始终不显示

    最近想学习mybatis的知识,自己搭了个简单的ssm框架,想通过插件自动生成实体类,发现想要的插件一直都没显示出来,着实很郁闷: pom.xm中的配置: <!--mybatis-generat ...

  9. mybatis generator配置,Mybatis自动生成文件配置,Mybatis自动生成实体Bean配置

    mybatis generator配置,Mybatis自动生成文件配置,Mybatis自动生成实体Bean配置 ============================== 蕃薯耀 2018年3月14 ...

随机推荐

  1. 转载:量化投资中常用python代码分析(一)

    pandas的IO 量化投资逃不过数据处理,数据处理逃不过数据的读取和存储.一般,最常用的交易数据存储格式是csv,但是csv有一个很大的缺点,就是无论如何,存储起来都是一个文本的格式,例如日期‘20 ...

  2. 在 Centos 安装 MySQL

    MySQL是开源的数据库管理系统,通常作为LEMP(Linux, Nginx, MySQL/MariaDB, PHP/Python/Perl)技术栈的一部分,而被安装.RedHat 会害怕 Oracl ...

  3. Linux 之父自传《just for fun》读书笔记

    一次偶然的机会,看到了阮一峰老师关于这本书的介绍,当时我就觉得这本书相当有趣. 在没有读这本书之前,我觉得 linus 作为发明 Linux 系统的人,应该是一个比较严肃的人,就像我的老师一样.但事实 ...

  4. python base64 decode incorrect padding错误解决方法

    个人觉得原因应该是不同的语言/base64库编码规则不太统一的问题. python中base64串的长度需为4的整数倍,故对长度不为4整数倍的base64串需要用"='补足 如下代码: da ...

  5. spring4笔记----spring生命周期属性

    init-method : 指定bean的初始化方法-spring容器会在bean的依赖关系注入完成后调用该方法 destroy-method :指定bean销毁之前的方法-spring容器将会在销毁 ...

  6. mysql建表基本语法

    mysql添加约束的两种条件: ------表的内部添加(约束) 列名1 数据类型 (int) primary key auto_increment,---主键默认不能为空的 列名2 数据类型 not ...

  7. Linux: yum 命令说明

    yum命令是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性 ...

  8. June 16. 2018, Week 24th. Saturday

    Success is the ability to go from one failure to another with no loss of enthusiasm. 成功,就是即使经历过一个又一个 ...

  9. June 3. 2018 Week 23rd Sunday

    You only get one shot; do not miss your chance to blow. 机会只有一次,不要错过. From Eminem, "Lose Yoursel ...

  10. 05.Python网络爬虫之三种数据解析方式

    引入 回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指 ...