zoukankan      html  css  js  c++  java
  • 【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 + "
    ";
    
                    }
                    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("动态编译失败:" + "
    " + 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 2012Addins下面,将T4Attribute.dll放在C:Program Files (x86)Microsoft Visual Studio 11.0Common7IDE下面,就可以打开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();
            }
        }

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

    小结

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

  • 相关阅读:
    js-html音乐播放
    CSS-实现倒影效果box-reflect
    html-屏蔽按键盘空格键是滚动条向下滚动
    html-禁用右键、键盘F12、网页上选取内容、复制、粘贴
    js-回调函数
    man bash 关于shell的应有尽有 语法、快捷键...
    source vs export AND ctrl d vs ctrl z
    技术----阳春
    限流 降级
    linux TOP
  • 原文地址:https://www.cnblogs.com/caizl/p/4524654.html
Copyright © 2011-2022 走看看