引言

记得第一次做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, , ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton); //将对应于该命令的控件添加到“工具”菜单:
if (command != null)
{
//command.AddControl(toolsPopup.CommandBar, 1);
command.AddControl(projBar, );
}
}
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(); if (item.Project != null)
{//被选中的就是项目本生
project = item.Project;
}
else
{//被选中的是项目下的子项
project = item.ProjectItem.ProjectItems.ContainingProject;
}
return project;
}
//获取选中文件全路径
private string GetSelecteditempath()
{ //从被选中对象中获取工程对象
EnvDTE.SelectedItem item = _applicationObject.SelectedItems.Item(); 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();
}
}

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

小结

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

最新文章

  1. jQuery拉开关闭帷幕
  2. REDIS 事务机制
  3. arm 基本
  4. [每日一题] OCP1z0-047 :2013-08-06 外表部――相关描述
  5. VS查看工程项目代码行数
  6. wuzhicms上传弹出层,如何返回数据到当前页面?
  7. UVa 10465 Homer Simpson (枚举)
  8. 执行eclipse,迅速failed to create the java virtual machine。
  9. Java并发编程之volatile变量
  10. Gridview编辑时Jquery自动计算自定义列(鼠标离开输入框Jquery计算)
  11. 最常用的动态sql语句梳理Mybatis(转)
  12. 自定义开关ToggleButton
  13. k60引脚图
  14. 最简单的方式离线部署Python依赖包
  15. mvc ajax访问后台时session过期无法跳转到Login页面问题解决
  16. Go Revel - Cache(缓存)
  17. ElectronNetTest
  18. 深入理解Linux内核-内存寻址
  19. PostgreSQL存储过程(2)-基于PL/PgSQL的存储过程
  20. 深圳MPD大会,五大专题一会尽享

热门文章

  1. Unity3D游戏开发从零单排(六) - 人物运动及攻击连击
  2. 001-Eclipse、idea集成javap查看字节码、javap说明
  3. mybatis-generator和TKmybatis的结合使用
  4. 20170411 F110初始界面-建议清单
  5. pandas(一)操作Series和DataFrame的基本功能
  6. 爬虫五 Beautifulsoup模块
  7. 运行docker image 忘记添加端口号
  8. Django 补充models操作,中间件, 缓存,信号,分页
  9. iOS 设置 延迟执行 与 取消延迟执行 方法 以及对 run loop 初步认识
  10. 根据GUID获取设备信息