【VS外接程序】利用T4模板生成模块代码

引言

     记得第一次做asp.net mvc项目时,可以用model直接生成Html的增删改查页面, 没什么特殊要求都可以不用修改直接用了, 觉得很神奇,效率太高了.后来在做客户端开发时,发现很多模块都是增删改查,于是打算做个类似的代码生成插件.琢磨了几天,用了一个比较奇异的思路做了出来,估计和mvc的有大不同.下面,简略地分享一下,写得比较乱,将就一下哈.

总体思路

    特性类->外接程序->T4模板->动态编译->生成文本文件->添加到当前项目

特性类

    简单起见,构建了两个特性,分别用于Class,Property,然后编译,得到T4Attribute.ll.代码如下

namespace T4Attribute
{
    [AttributeUsage(AttributeTargets.Class)]
    public class T4ClassAttribute : Attribute
    {
        public string Author;  //开发者名字

        public T4ClassAttribute(string author)
        {
            this.Author = author;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class T4PropertyAttribute : Attribute
    {
        public string DisplayName;  //打印的名字
        public bool IsOutput;  //是否打印

        public T4PropertyAttribute(string DisplayName, bool IsOutput)
        {
            this.DisplayName = DisplayName;
            this.IsOutput = IsOutput;
        }
    }
}

创建外接程序

     在vs2012的新建项目-其他项目类型-扩展性,可以找外接程序的项目模板,选择后,有向导界面弹出来,注意下图的选项,勾上就行,其他的随意,记得添加刚才的T4Attribute.ll引用

T4模板

    在外接程序的项目中,添加新项,找到运行时文本模板,创建即可,保存后会看到TT文件下生成了一个cs文件, 代码如下:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="T4Attribute.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace= "T4Attribute" #>
<#@ parameter type="System.Object" name="model" #>
<#@ output extension=".cs" #>
<#
    T4ClassAttribute  ModelAttribute =(T4ClassAttribute)model.GetType().GetCustomAttributes(typeof(T4ClassAttribute), false).FirstOrDefault();
 #>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace <#=        model.GetType().Namespace#>
{

 /// <summary>
    ///  创建人:<#=        ModelAttribute.Author #>
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {

 <#    foreach ( var item in  model.GetType().GetProperties())  
    {     
        if(((T4PropertyAttribute)item.GetCustomAttributes(typeof(T4PropertyAttribute), false).FirstOrDefault()).IsOutput){
 #>
        Console.WriteLine("<#= ((T4PropertyAttribute)item.GetCustomAttributes(typeof(T4PropertyAttribute), false).FirstOrDefault()).DisplayName#>");

<#    }} #>
          
            Console.ReadLine();
        }
    }
}

然后再添加一个类文件,它是刚才T4模板生成的部分类,作用是给模板类提供一个参数输入的构造函数,代码如下:

namespace  MyCodeAddin.T4
{
    public partial class ConsoleCode
    {

        public ConsoleCode(object model)
        {
            _modelField = model;   
        }
    }
}

 动态编译

     动态编译的目的是将我们选中的model文件(.cs)实例化,传给T4模板类,生成最终cs文件,代码如下:

 class DynamicCompiling
    {
        public static object Compo(string code)
        {
            string liststr = "";
            // 1.CSharpCodePrivoder
            CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

            // 2.ICodeComplier
            ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();

            // 3.CompilerParameters
            CompilerParameters objCompilerParameters = new CompilerParameters();
            objCompilerParameters.ReferencedAssemblies.Add("System.dll");
            objCompilerParameters.ReferencedAssemblies.Add("System.Core.dll");
            objCompilerParameters.ReferencedAssemblies.Add(Application.StartupPath + "\\T4Attribute.dll");
            objCompilerParameters.GenerateExecutable = false;
            objCompilerParameters.GenerateInMemory = true;

            // 4.CompilerResults
            CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, code);

            if (cr.Errors.HasErrors)
            {
                foreach (CompilerError err in cr.Errors)
                {
                    liststr = liststr + err.ErrorText + "\r\n";

                }
                return liststr;
            }
            else
            {
                // 通过反射,调用objmodel的实例
                Assembly objAssembly = cr.CompiledAssembly;
                object objmodel = objAssembly.CreateInstance(objAssembly.GetTypes().FirstOrDefault().ToString());
                return objmodel;
            }
        }
    }

 继续外接程序

     接着,要在项目中的Connect.cs实现我们的代码生成.实现3个方法OnConnection,QueryStatus,Exec,代码如下

public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2)application;
            _addInInstance = (AddIn)addInInst;
            if (connectMode == ext_ConnectMode.ext_cm_UISetup)
            {
                object[] contextGUIDS = new object[] { };
                Commands2 commands = (Commands2)_applicationObject.Commands;


                CommandBars cbs = (CommandBars)_applicationObject.CommandBars;
                CommandBar projBar = cbs["Item"];
                //如果希望添加多个由您的外接程序处理的命令,可以重复此 try/catch 块,
                //  只需确保更新 QueryStatus/Exec 方法,使其包含新的命令名。
                try
                {
                    //将一个命令添加到 Commands 集合:
                    Command command = commands.AddNamedCommand2(_addInInstance, "MyCodeAddin", "MyCodeAddin", "Executes the command for MyCodeAddin", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);

                    //将对应于该命令的控件添加到“工具”菜单:
                    if (command != null)
                    {
                        //command.AddControl(toolsPopup.CommandBar, 1);
                        command.AddControl(projBar, 1);
                    }
                }
                catch (System.ArgumentException e)
                {

                    MessageBox.Show(e.Message);
                    //如果出现此异常,原因很可能是由于具有该名称的命令
                    //  已存在。如果确实如此,则无需重新创建此命令,并且
                    //  可以放心忽略此异常。
                }
            }
        }

        /// <summary>实现 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新该命令的可用性时调用</summary>
        /// <param term='commandName'>要确定其状态的命令的名称。</param>
        /// <param term='neededText'>该命令所需的文本。</param>
        /// <param term='status'>该命令在用户界面中的状态。</param>
        /// <param term='commandText'>neededText 参数所要求的文本。</param>
        /// <seealso class='Exec' />
        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
        {

            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                if (commandName == "MyCodeAddin.Connect.MyCodeAddin")
                {
                    if (!GetSelecteditempath().ToLower().EndsWith(".cs"))
                    {
                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusInvisible;

                    }
                    else
                    {
                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                    }
                    return;
                }
            }
        }

        /// <summary>实现 IDTCommandTarget 接口的 Exec 方法。此方法在调用该命令时调用。</summary>
        /// <param term='commandName'>要执行的命令的名称。</param>
        /// <param term='executeOption'>描述该命令应如何运行。</param>
        /// <param term='varIn'>从调用方传递到命令处理程序的参数。</param>
        /// <param term='varOut'>从命令处理程序传递到调用方的参数。</param>
        /// <param term='handled'>通知调用方此命令是否已被处理。</param>
        /// <seealso class='Exec' />
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
        {
            handled = false;
            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
            {
                if (commandName == "MyCodeAddin.Connect.MyCodeAddin")
                {
                    if (!GetSelecteditempath().ToLower().EndsWith(".cs"))
                    {
                        return;
                    }
                    //读取选中类文件
                    FileStream fs = new FileStream(GetSelecteditempath(), FileMode.Open, FileAccess.Read);
                    StreamReader sr = new StreamReader(fs, Encoding.GetEncoding("GB2312"));
                    //将文件内容编译生成对象
                    string conString = sr.ReadToEnd();
                    object classobject = DynamicCompiling.Compo(conString);

                    string aa = classobject.GetType().Namespace;

                    if (classobject is string)
                    {
                        MessageBox.Show("动态编译失败:" + "\r\n" + classobject, "类文件编译错误!");
                        sr.Close();
                        fs.Close();
                        handled = true;
                        return;
                    }

                    //创建代码文件,并添加到项目中
                    Createcode(classobject, GetSelectedProject(), GetSelectedProjectPath());

                    sr.Close();
                    fs.Close();

                    handled = true;
                   
                }
            }
        }

        //创建viewmodel代码文件,并添加到项目
        public static ProjectItem Createcode(object model, Project project, string path)
        {
            ConsoleCode consoleCode = new ConsoleCode(model);
            string codetext = consoleCode.TransformText();
            //如果不存在文件夹,则创建
            string Projectpath = path;
            if (!Directory.Exists(Projectpath))
            {
                Directory.CreateDirectory(Projectpath);
            }
            //将目标代码生成文件
            string createpath = Projectpath +  "Program.cs";
            FileStream fr = new FileStream(createpath, FileMode.Create);
            StreamWriter sw = new StreamWriter(fr);
            sw.Write(codetext);
            sw.Close();
            fr.Close();
            //添加文件到项目中
            return project.ProjectItems.AddFromFile(createpath);

        }


        //获取选中所属项目
        private Project GetSelectedProject()
        {
            Project project = null;
            //从被选中对象中获取工程对象 
            EnvDTE.SelectedItem item = _applicationObject.SelectedItems.Item(1);

            if (item.Project != null)
            {//被选中的就是项目本生 
                project = item.Project;
            }
            else
            {//被选中的是项目下的子项 
                project = item.ProjectItem.ProjectItems.ContainingProject;
            }
            return project;
        }
        //获取选中文件全路径
        private string GetSelecteditempath()
        {

            //从被选中对象中获取工程对象 
            EnvDTE.SelectedItem item = _applicationObject.SelectedItems.Item(1);

            string selectpath = item.ProjectItem.Properties.Item("FullPath").Value.ToString();

            return selectpath;
        }

        //获取选中文件所属项目的路径,不含文件名
        private string GetSelectedProjectPath()
        {
            string path = "";
            //获取被选中的工程 
            Project project = GetSelectedProject();
            if (project != null)
            {
                //全名包括*.csproj这样的文件命 
                path = project.FullName;
            }
            //去掉工程的文件名 

            path = project.FullName.Replace(project.Name + ".csproj", "");

            return path;
        } 

如何使用

    将上面的工程编译后,在项目目录下得到MyCodeAddin.AddIn,MyCodeAddin.dll,T4Attribute.dll,我们将这三个文件放在我的文档\Visual Studio 2012\Addins下面,将T4Attribute.dll放在C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE下面,就可以打开vs2012了,没有意外的话会工具-外接程序中看到我们的插件.OK,让我们来测试一下吧,新建控制台项目,删掉Program.cs文件,添加Test.cs文件,代码如下

 [T4Class( "Czl")]
    class Test
    {
        [T4Property("名字",true)]
        public string Name { get; set; }
        [T4Property("部门", true)]
        public string Dept { get; set; }
        [T4Property("地址", false)]
        public string Address { get; set; }
    }

然后右键Test.cs文件,会看到一个按钮[MyCodeAddin],点击它后,会看到Program.cs已经自动添加到项目中了,代码如下

/// <summary>
    ///  创建人:Czl
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {

         Console.WriteLine("名字");

        Console.WriteLine("部门");

          
            Console.ReadLine();
        }
    }

然后,启动,看看结果吧.

小结

     回头看看,发现我还是在一本正经地胡说八道.示例比较简单,但是好歹也算是一个代码生成的示范了.事实上,我已经用这种方式编写了能生成完整增删改查模块代码的外接程序(生成代码后能马上编译启动那种).我想,在那些比较固定化的模块中,用代码生成是比较合适的,毕竟效率妥妥的.最后,应该有更好的方式实现代码生成的,大方的你可以指教一下我啊,拜谢.

转载于:https://www.cnblogs.com/caizl/p/4524654.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: VS Code T4 模板是一种用于生成 JavaScript 代码的工具。它基于 T4(Text Template Transformation Toolkit)技术,允许开发者定义模板并在生成代码时将其应用于指定的数据。 使用 VS Code T4 模板生成 JavaScript 代码非常简单。首先,我们需要安装 "T4" 扩展插件,该插件提供了 T4 模板引擎的支持。安装完成后,我们可以创建一个新的 T4 模板文件,并在其中编写模板代码T4 模板使用一种特殊的标记语法,类似于 HTML 的标签。我们可以在模板中定义变量、循环、判断等逻辑,并根据需要生成相应的 JavaScript 代码模板还可以包含外部引用的文件或库,以便在代码生成过程中使用。 一旦模板编写完成,我们可以在模板文件上右键点击,并选择 "运行 T4 模板" 选项,或使用命令面板搜索并运行相应命令。这将触发模板引擎执行,根据模板和提供的数据生成 JavaScript 代码T4 模板可以根据我们定义的逻辑和数据生成任意数量的 JavaScript 代码文件。这为我们提供了一种自动化生成重复代码的方式,在项目开发中可以极大地提高效率和代码质量。 总之,VS Code T4 模板提供了一种方便的方式来生成 JavaScript 代码。通过定义模板和提供相应的数据,我们可以自动生成符合需求的 JavaScript 代码,减少重复工作并提高开发效率。无论是创建新项目、添加新功能还是生成测试数据,T4 模板都能为我们提供一种强大的辅助工具。 ### 回答2: VSCode是一款功能强大的文本编辑器,它提供了许多扩展插件,其中包括T4模板生成JS。T4模板是一种用于生成代码模板引擎,它使用C#或VB.NET编写模板代码,并通过内置的代码生成引擎将模板代码转换为最终的输出。 在VSCode中使用T4模板生成JS非常简单。首先,我们需要安装T4模板插件。可以通过在VSCode的扩展商店中搜索"T4 Template"来找到并安装这个插件。安装完成后,我们可以在VSCode的侧边栏中找到T4模板生成器的图标。 接下来,我们需要创建一个T4模板文件。可以使用`.tt`作为扩展名来命名文件,并打开该文件进行编辑。在模板文件中,我们可以使用C#或VB.NET编写代码块,并使用特定的语法来控制模板生成的过程。我们可以定义输入参数、循环结构、条件语句等,以生成不同的输出内容。 在T4模板生成JS代码的过程与生成其他类型的代码并无太大区别。我们可以在模板中定义JS变量、函数、类等,并使用T4模板提供的代码生成功能来生成相应的JS代码。 当模板文件中的代码编写完成后,我们可以保存文件并触发模板生成过程。在VSCode的命令面板中,输入"T4 Template"并选择生成模板命令,就可以将模板文件转换为对应的JS代码文件。 总结来说,使用VSCode的T4模板插件可以方便地生成JS代码。我们只需要编写模板文件,定义所需的JS代码结构和逻辑,然后由T4模板生成器自动转换为最终的JS代码文件。这样,我们可以更高效地生成大量的JS代码,并减少手工编写的工作量。 ### 回答3: VSCode是一款功能强大的代码编辑器,它支持通过T4模板生成JavaScript代码T4模板是一种文本生成引擎,它允许我们根据定义的模板规则生成特定的代码文件。在VSCode中使用T4模板生成JavaScript代码可以提高开发效率和代码质量。 首先,我们需要安装VSCode的T4模板插件,可以在VSCode的插件市场中搜索并安装。安装完成后,我们可以在VSCode的扩展面板中找到并启用T4模板相关功能。 接下来,在VSCode中创建一个新的文件,并将其命名为T4模板文件,通常以.tt作为文件扩展名。在T4模板文件中,我们可以使用类似于ASP.NET的标签语法来定义模板中的输入和输出。 在模板文件中,我们可以定义输入参数,例如要生成的JavaScript文件的名称和路径。然后,可以使用T4模板提供的标签和指令,通过循环、条件语句和字符串操作等方式来生成JavaScript代码。 使用T4模板生成JavaScript代码的好处是我们可以根据不同的需求和条件生成不同的代码。这样可以提高代码的可维护性和复用性。同时,T4模板还支持自定义函数和引用外部库,可以更灵活地生成JavaScript代码。 最后,我们可以通过执行模板文件来生成JavaScript代码。在VSCode中,我们可以打开T4模板文件,右键点击并选择“转换为输出文件”,这将会根据模板文件的定义生成相应的JavaScript代码文件。 总结起来,通过VSCode的T4模板插件,我们可以方便地生成JavaScript代码。通过定义模板规则和执行模板文件,我们可以根据需求快速生成高质量的JavaScript代码。这对于JavaScript开发者来说是一个非常有用的工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值