目录
3.1 使用VSIX+ProjectTemplate创建项目模板
0.引言
最近一直在学习博客【老张的哲学】的.NET Core2.0 Api + Vue 2.0的系列文章,通过边看边自己动手实践,自己对一些概念有了更深入的理解(如依赖注入、前端的一些知识、VUE框架),本来也计划好通过该系列文章好好学习一下VUE框架并应用到实际的项目中的,特此还重点突击了一个星期左右,但最后发现自身功力不够(毕业3年都没有敲过代码,加之之前只是做过一些win form的简单开发,Web知识还是比较欠缺的,由于工作的需要今年才重新捡起来),所以是后还是放弃了。哈哈,扯远了......下面开始进入正题
在【老张的哲学】的系列教程第32篇博文中博主主要介绍了快速实现项目的半自动化搭建的四种方式:动软代码生成器、VSTO、T4模板、SqlSuagr等,按我的理解我觉得这几种方式,无非就是事先定义好一些模板,然后按各自的方法生成相应的文件(好像这句话等于没有说,哈哈.....),这几种方式确实在实际的工作中可以减少很多重复性的工作,而且自己通过这大半年的开发工作中,也深有感触是很有必要找到一种适合的方式来减少这些不必要的工作量(不瞒大家说,也不怕大家笑话,最近的项目开发中的CRUD真的是我自己一行一行敲出来的),今天的话主要在博主的基础上在介绍另外一种方式:VSIX插件+ItemTemplate+ProjectTemplate来实现同样的目的,这里的话主要是介绍基础的,后面在根据项目的需要完善。本人也没有写过技术博客,如果写得不好或有误,请大家多多包含和批评指正。
1.什么是Visual Studio项目模板
说了那么多,大家可能还是比较蒙圈(也可能是我没有解释清楚),直接来3张图(如下所示),在平常的开发过程中,大家肯定经常通过在项目中右键创建类、接口、Windows窗体、用户控件等,其实这些就是Visual Studio自身提供的一些模板,如新建一个Windows窗体后,窗体的大小、颜色、显示的名称都已经定义好了,不需要用户在敲一大堆代码才能把这个窗体显示出来,本文就是通过这种方式快速实现项目的半自动化搭建。
【新建项】
【新建Windows窗体】
【Form1】
2.IWizad接口
在开始操作前,首先说说“IWizard”接口:
Visual Stdudio提供了IWizard接口,该接口提供了几个方法帮助用户在创建模板项时可运行自定义的代码,见下表,本文就是通过这个接口实现的。
Name | Description | |
---|---|---|
![]() |
BeforeOpeningFile(ProjectItem) |
Runs custom wizard logic before opening an item in the template. |
![]() |
ProjectFinishedGenerating(Project) |
Runs custom wizard logic when a project has finished generating. |
![]() |
ProjectItemFinishedGenerating(ProjectItem) |
Runs custom wizard logic when a project item has finished generating. |
![]() |
RunFinished() |
Runs custom wizard logic when the wizard has completed all tasks. |
![]() |
RunStarted(Object, Dictionary<String, String>, WizardRunKind, Object[]) |
Runs custom wizard logic at the beginning of a template wizard run. |
![]() |
ShouldAddProjectItem(String) |
Indicates whether the specified project item should be added to the project. |
3.通过Visual Studio扩展开发实现领域驱动开发
3.1 使用VSIX+ProjectTemplate创建项目模板
什么是领域驱动开发,哈哈......有兴趣的可以去深入了解一下,之所以提起这个概念,主要是最近在Github上了解到ABP这个开源项目,目前start的人数多达5.5k,网址asp.net boilerplate,这个框架比较好的实现了领域驱动开发这个概念(据说也不是完全符合领域驱动设计的概念,具体的话还需要自己深入了解学习),自我认为这是个比较好的学习框架,里面涉及到比较多新的技术和知识(对于我来说基本上是新知识),大家有时间可以抽空看看,后期我也计划把该框架应用到实际的项目中去,哈哈......又扯远了。
下面正式进入正题:
1.创建项目模板,命名为FirstProjectTemplate。并打开该项目下的Class1.cs文件,随意输入一些注释,有啥用?请耐心等待。
备注:如果没有Extensibility这个分类,请在更新与扩展里安装。
2.添加新的VSIX项目,命名为FirstProjectWizard,并把该项目设置为启动项目。
3.在FirstProjectWizard项目中双击打开source.extension.vsixmaifest文件,填写Product Name、Author、Version、Desription等基本信息。
4.在3打开的文件中,在左侧栏中切换到Assets选项卡,单击【New】新建一个Asset(中文不知道翻译成啥好,哈哈...),Type选择Microsoft.VisualStudio.ProjectTemplate,Source选择A Project in current solution,Project选择FirstProjectTemplate,其它设置见图。
5.按F5 生成解决方案并启动调试,将打开另一个Visual Studio实例(这可能需要几分钟的时间,电脑配置好点估计秒开...),仔细的估计会注意到Visual Studio窗口多了【实验实例】几个字,这个具体拿来干啥的,有兴趣的可以百度一下,哈哈...
6.在刚刚打开的Visual Sdudio窗口新建一个项目,弹出的【新建项目】窗口,在右上角输入first,选中FirstProjectTemplate,单击确定。
7.打开刚新建项目中的Class1.cs,哦哦...刚在1步骤中注释的语句出现了,是不是尝到了胜利的味道。或许说到这里,大家已经大概了解到思路了,恭喜你离成功更靠近一步了....加油,请给点耐心哟。
3.2使用ItempTemplate创建自定义项模板
本节主要介绍通过自定义一些参数,实现在添加项时项模板能按要求输出,好了...话不多说,直接演示。
1.在3.1创建的解决方案中,新建一个项项目(ItemTemplate),命名FirstItemTemplate。
2.选中3.1中创建的FirstProjectWizard项目,按F4弹出属性窗口,设置如图三个属性字段为ture。
3.将FirstProjectWizard、FirstItemTemplate作为Asset添加到VSIX项目中。双击打开source.extension.vsixmanifest 文件,并切换到Asset窗口中,相关设置见下图。
【添加FirstProjectWizard】
【添加FirstItemTemplate】
【添加后的Asset】
4.添加VSIX开发要用到的相关类库。在FirstProjectWizard项目下的引用鼠标右键,添加
EnvDTE、Microsoft.VisualStudio.TemplateWizardInterface、System.Drawing、System.Windows、System.Windows.Forms等。
5.在FirstProjectWizard项目中添加类,并命名为WizardImplementation,并继承IWizard接口,具体代码如下:
-
using EnvDTE;
-
using Microsoft.VisualStudio.TemplateWizard;
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
using System.Threading.Tasks;
-
using System.Windows.Forms;
-
-
namespace FirstProjectWizard
-
{
-
public class WizardImplementation : IWizard
-
{
-
private CustomFieldForm customFieldForm;
-
private string customField;
-
-
public void BeforeOpeningFile(ProjectItem projectItem)
-
{
-
-
}
-
-
public void ProjectFinishedGenerating(Project project)
-
{
-
-
}
-
-
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
-
{
-
-
}
-
-
public void RunFinished()
-
{
-
-
}
-
-
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
-
{
-
try
-
{
-
customFieldForm = new CustomFieldForm();
-
customFieldForm.ShowDialog();
-
-
customField = CustomFieldForm.CustomField;
-
-
//添加自定义参数
-
replacementsDictionary.Add("customField",
-
customField);
-
}
-
catch (Exception ex)
-
{
-
MessageBox.Show(ex.ToString());
-
}
-
}
-
-
public bool ShouldAddProjectItem(string filePath)
-
{
-
return true;
-
}
-
}
-
}
6.在FirstProjectWizard项目中添加Windows窗体,命名为CustomFieldForm,具体代码如下:
-
using System;
-
using System.Collections.Generic;
-
using System.ComponentModel;
-
using System.Data;
-
using System.Drawing;
-
using System.Linq;
-
using System.Text;
-
using System.Threading.Tasks;
-
using System.Windows.Forms;
-
-
namespace FirstProjectWizard
-
{
-
public partial class CustomFieldForm : Form
-
{
-
private static string customField;
-
-
public CustomFieldForm()
-
{
-
InitializeComponent();
-
}
-
-
public static string CustomField
-
{
-
get
-
{
-
return customField;
-
}
-
set
-
{
-
customField = value;
-
}
-
}
-
-
private void button1_Click(object sender, EventArgs e)
-
{
-
customField = textBox1.Text;
-
}
-
}
-
}
7.添加程序集签名。选中FirstProjectWizard项目,右键单击,选中项目属性,切换到签名栏,勾选为程序集签名,输入秘钥文件名称key.snk,去掉勾选使用密码保护秘钥文件,相关设置见下图。
8.选中FirstProjectWizard项目,按F4弹出属性窗口,设置复制生成输出到输出目录字段true,并重新生成解决方案。
9.将刚生成的key.snk秘钥文件拷贝到C:Program Files (x86)Microsoft SDKsWindowsv10.0AinNETFX 4.6.1 Tools目录下,运行cmd(需要管理员身份运行),并cd到C:Program Files (x86)Microsoft SDKsWindowsv10.0AinNETFX 4.6.1 Tools该目录下,输入:
sn.exe -p key.snk outfile.key
10.从key.snk中获取公钥,在命令窗口输入,如图 并记下该公钥。
sn.exe -t outfile.key
11.将对自定义向导的引用添加到项目模板的.vstemplate 文件。分别找到FirstItemTemplate、FirstProjectTemplate项目中的两个vstemplate文件,在<TemplateContent>节点后面添加如下代码,最终如图。
-
<WizardExtension>
-
<Assembly>FirstProjectWizard, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=df318ff0a21a5d6d</Assembly>
-
<FullClassName>FirstProjectWizard.WizardImplementation</FullClassName>
-
</WizardExtension>
12.保存所有文件,并重新生成解决方案。按F5运行,打开刚刚创建的FirstProjectTemplate1项目,右键添加新项,弹出【添加新项】窗口,在右上角搜索first,哈哈...FirstItemTemplate出现啦...成功八九十
3.3实现可视化添加实体字段,自动生成应用层(Dto、IAppService、AppService)、领域层(Entity)、展现层(Views、Controller)、基础设施层(IRepository)等。
好啦,通过3.1、3.2的介绍基本的框架已经搭建起来啦,下面就在ABP架构的基础上实现通过可视化添加实体,快速自动生成领域驱动设计各层的部分文件,注意只是部分哈,不要纠结是否符合领域驱动设计。
在开始前,首先介绍一下【模板参数】,见下表,replacementsDictionary共有23个系统自带的参数,通过键值(Dictionary)保存,当然用户可以通过自定义参数添加到字典中,具体有什么作用,请见后文。
参数 | 描述 |
---|---|
clrversion |
公共语言运行时 (CLR) 的当前版本。 |
GUID [1-10] |
一个用于替换项目文件中的项目 GUID 的 GUID。 可指定最多 10 个唯一的 GUID(例如,guid1) 。 |
itemname |
“添加新项”对话框中由用户提供的名称。 |
machinename |
当前的计算机名称(例如,Computer01)。 |
projectname |
“新建项目”对话框中由用户提供的名称。 |
registeredorganization |
来自 HKLMSoftwareMicrosoftWindows NTCurrentVersionRegisteredOrganization 的注册表项值。 |
rootnamespace |
当前项目的根命名空间。 此参数仅适用于项模板。 |
safeitemname |
用户在“添加新项”对话框中提供的名称,名称中移除了所有的不安全字符和空格。 |
safeprojectname |
用户在“新建项目”对话框中提供的名称,名称中移除了所有的不安全字符和空格。 |
time |
以 DD/MM/YYYY 00:00:00 格式表示的当前时间。 |
SpecificSolutionName |
解决方案的名称。 在选中“创建解决方案目录”时,SpecificSolutionName 具有解决方案名称。 在未选中“创建解决方案目录”时,SpecificSolutionName 为空。 |
userdomain |
当前的用户域。 |
username |
当前的用户名称。 |
webnamespace |
当前网站的名称。 此参数在 Web 窗体模板中用于保证类名是唯一的。 如果网站在 Web 服务器的根目录下,则此模板参数会解析为 Web 服务器的根目录。 |
year |
以 YYYY 格式表示的当前年份。 |
1.在FirstItemTemplate项目中,双击打开vstemplate文件,找到文件自动生成的<ProjectItem>,随后跟着添加多几个<ProjectItem>,具体如下:
-
<!--safeitemname 用户在“添加新项”对话框中提供的名称,名称中移除了所有的不安全字符和空格-->
-
<ProjectItem ReplaceParameters="true" TargetFileName="$safeitemname$.cs">CoreEntity.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="TempI$safeitemname$AppService.cs">ApplicationIAppService.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="Temp$safeitemname$AppService.cs">ApplicationAppService.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="Temp$safeitemname$Dto.cs">ApplicationEntityDto.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="Temp$safeitemname$ListDto.cs">ApplicationEntityListDto.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="TempIndex.cshtml">UIIndex.cshtml</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="TempIndex.js">UIIndex.js</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="Temp$safeitemname$Controller.cs">UIController.cs</ProjectItem>
-
<ProjectItem ReplaceParameters="true" TargetFileName="TempI$safeitemname$Repository.cs">InfrastructureIRepository.cs</ProjectItem>
2.对照在1中添加的配置新建相应文件,如Entity.cs、IAppService.cs,最终结构见下图。
3.利用模板参数replacementsDictionary实现自定义项模板,如Entity.cs类如下图,其它按照自己的需求实现,思路就是用replacementsDictionary里的值进行替换,如果大家有更好的思路,欢迎大家提出。
-
using Abp.Domain.Entities;
-
using System;
-
using System.Collections.Generic;
-
using System.ComponentModel.DataAnnotations.Schema;
-
using System.Text;
-
-
namespace $rootnamespace$
-
{
-
[]
-
public class $safeitemname$ : Entity<long>, IMayHaveTenant, IPassivable
-
{
-
//实现IMayHaveTenant,IPassivable
-
public virtual int? TenantId { get; set; }
-
public virtual bool IsActive { get; set; }
-
-
//自定义的字段
-
public virtual string $customField$ {get;set;}
-
}
-
}
4.在WizardImplementation类的ProjectItemFinishedGenerating( )方法中,实现文件的放到指定的工程目录下。PS:目前的方法比较笨,主要实现思路是先根据模板生成文件,然后Copy一份到指定的目录,然后把源文件Delete。大家如有好的方法,请多多留言。代码如下:
-
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
-
{
-
//服务层
-
if (projectItem.Name.IndexOf("AppService") > 0)
-
{
-
if (!projectItem.IsOpen) projectItem.Open();
-
-
foreach (var item in _dte.Solution.Projects)
-
{
-
var project = (Project)item;
-
//找到服务层的项目
-
if (project.Name.IndexOf("Application") > 0)
-
{
-
bool flagAppService = false;
-
ProjectItem folder = null;
-
foreach (var folderItem in project.ProjectItems)
-
{
-
var tmp = (ProjectItem)folderItem;
-
if (tmp.Name.IndexOf("AppService") > -1)
-
{
-
flagAppService = true;
-
folder = tmp;
-
}
-
}
-
//不存在文件夹则创建
-
if (!flagAppService)
-
folder = project.ProjectItems.AddFolder("AppService");
-
-
folder.ProjectItems.AddFromFileCopy(projectPath + @"AppService" + projectItem.Name);
-
}
-
}
-
}
-
}
5.继续按F5运行,打开Visual Studio【实验实例】,新建一个解决方案,包含两个工程,如下图:
6.在CenterInfo.Core工程中右键新建项,弹出添加新项对话框,找到FirstItemTemplate,命名为Depart。
7.弹出创建【Depart】实体对话框,这里主要模拟创建包含Name字段的实体类,单击确定。
8.哇...最终生成如下图,搞了半天就这么点东西。
4.结语
终于写到结语了,说实话写个技术博客真不容易(这篇博客花了2天的时间收集资料,1天的时间自己动手实践,然后几个小时的时间完成编写)......在第3小节说好的【通过Visual Studio扩展开发实现领域驱动开发】,哈哈,都是骗人的,只是为了吸引大家的眼球,好啦,坑我就挖到这里,剩下的由大家把这个坑补完。
(后续有时间会把【通过Visual Studio扩展开发实现领域驱动开发】实现,期待着...)