1. 程式人生 > >利用Visual Studio 2017的擴充套件開發(VSIX、ItemTemplate) 快速實現專案的半自動化搭建

利用Visual Studio 2017的擴充套件開發(VSIX、ItemTemplate) 快速實現專案的半自動化搭建

目錄

0.引言

1.什麼是Visual Studio專案模板

2.IWizad介面

3.通過Visual Studio擴充套件開發實現領域驅動開發

3.1 使用VSIX+ProjectTemplate建立專案模板

3.2使用ItempTemplate建立自定義項模板

3.3實現視覺化新增實體欄位,自動生成應用層(Dto、IAppService、AppService)、領域層(Entity)、展現層(Views、Controller)、基礎設施層(IRepository)等。

4.結語


文章是有圖片的,如果圖片打不開請訪問CSDNhttps://blog.csdn.net/lynchee/article/details/83065608

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
System_CAPS_pubmethod BeforeOpeningFile(ProjectItem)

Runs custom wizard logic before opening an item in the template.

System_CAPS_pubmethod ProjectFinishedGenerating(Project)

Runs custom wizard logic when a project has finished generating.

System_CAPS_pubmethod ProjectItemFinishedGenerating(ProjectItem)

Runs custom wizard logic when a project item has finished generating.

System_CAPS_pubmethod RunFinished()

Runs custom wizard logic when the wizard has completed all tasks.

System_CAPS_pubmethod RunStarted(Object, Dictionary<String, String>, WizardRunKind, Object[])

Runs custom wizard logic at the beginning of a template wizard run.

System_CAPS_pubmethod 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 SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools目錄下,執行cmd(需要管理員身份執行),並cd到C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 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 來自 HKLM\Software\Microsoft\Windows NT\CurrentVersion\RegisteredOrganization 的登錄檔項值。
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">Core\Entity.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\I$safeitemname$AppService.cs">Application\IAppService.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\$safeitemname$AppService.cs">Application\AppService.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\$safeitemname$Dto.cs">Application\EntityDto.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\$safeitemname$ListDto.cs">Application\EntityListDto.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\Index.cshtml">UI\Index.cshtml</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\Index.js">UI\Index.js</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\$safeitemname$Controller.cs">UI\Controller.cs</ProjectItem>
    <ProjectItem ReplaceParameters="true" TargetFileName="Temp\I$safeitemname$Repository.cs">Infrastructure\IRepository.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$
{
    [Table("Abp$safeitemname$s")]
    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擴充套件開發實現領域驅動開發】實現,期待著...)