编写T4模板进行代码生成无法避免的两个话题:"Assembly Locking"&"Debug"

在这之前,我写了一系列关于代码生成和T4相关的文章,而我现在也试图将T4引入我们自己的开发框架。在实践中遇到了一些问题,也解决了不少问题。如果你也在进行T4相关的开发,相信你也一定会遇到这些问题。为此,特意将这些问题和解决方案与朋友们分享,希望在遇到这些问题的时候少走弯路。本篇文章介绍的是两个重要的话题:程序集锁定和调试。

目录
一、程序集引用导致的编译问题
二、T4引擎对引用程序集的锁定
三、Debugger.Break导致VS 2010的Crash
四、在Debugger.Break之前加上Debugger.Launch

一、程序集引用导致的编译问题

为了让读者对“程序集锁定”,以及由它造成的开发上的不便有一个深刻的认识,我特意写了一个小例子。如右图所示的解决方案包含两个项目:Lib和T4。其中我们的T4项目中定义了一个叫作HelloWorld.tt的模板文件,该文件需要使用到定义在Lib项目中的某个类型。所以,HelloWorld.tt模板文件中需要通过<#@Assembly…#>指令引用Lib项目编译生成的程序集(Artech.T4Template.Lib.dll)。

如果你看过我上一篇文章,你应该知道我们至少具有解决T4模板的程序集引用的五种方案,在这里我们采用的是VS宏的解决方案,即将引用程序集文件的路径设置成通过$(SolutionDir)表示的解决方案目录的相对路径。HelloWorld.tt定义如下,引用的程序集路径为Lib项目在Debug模式下编译生成的目录($(SolutionDir)Lib\Bin\Debug\)。

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
using System;
public class HelloWord
{
    static void Main()
    {    
        <# foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
        {#>
        Console.WriteLine("Hello, {0}!", "<#=person#>");    
        <# } #>
    }
}

当你保存该T4模板,T4引擎将触发并进行代码生成工作,但是此时如果你试图编译被引用(实际上是生成的程序集被引用)的Lib项目,将会出现如下所示的编译错误。错误信息为:“Unable
to copy file "obj\Debug\Artech.T4Template.Lib.dll" to
"bin\Debug\Artech.T4Template.Lib.dll". The process cannot access the
file 'bin\Debug\Artech.T4Template.Lib.dll' because it is being used by
another process.”,即之前生成的程序集正在被使用,所以不能将生成的程序集拷贝到编译目标目录下。

二、T4引擎对引用程序集的锁定

实际上这个程序集的使用者正是T4引擎。出于提高性能考虑,T4引擎在进行基于代码生成的模板转换(Template Transformation)的时候,会始终重用同一个AppDomain。由于该AppDomain不会自动卸载,这就会导致该AppDomain始终锁定所有被它加载的程序集。如果我们需要释放程序集,我们不得不重启VS。但是,对于T4模板的开发调试阶段,这种通过重新启动VS的方式去释放程序集以确保我们的项目能够成功编译是不能接受的。

那么,是否有一种解决方案既能够确保T4引擎能够进行正常的模板转换,又能避免它强行锁定引用程序集呢?如果你采用T4 ToolBox,你可以通过<#@ VolatileAssembly…#>这个指令轻松地解决这个问题。下面的T4模板中,我们将通过<#@Assembly…#>指令的程序集引用方式替换成了<#@ VolatileAssembly…#>(<#@
VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" 
name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll"
#>),我们的Lib项目在任何时候都可以自由地编译。

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"  name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
using System;
public class HelloWord
{
    static void Main()
    {    
        <# foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
        {#>
        Console.WriteLine("Hello, {0}!", "<#=person#>");    
        <# } #>
    }
}

<#@ VolatileAssembly…#>的实现原理其实挺简单的,就是在加载的时候并不是直接加载指定的源程序集,而是创建一个新的程序集拷贝。

三、Debugger.Break导致VS 2010的Crash

VS和一些T4编辑器虽然给了基本的智能感知支持,但是在绝大部分我们相当于在编写纯文本的脚本,所以对于一些比较复杂的模板转换逻辑,我们需要通过Debug的方式去发现一些无法避免的问题。关于T4模板的Debug,你Google一下会搜出一大堆。在这些“大众化”的Debug解决方案中都包含两点:

  • 在<#@ template…#>指令中将debug属性设置为true;
  • 在需要设置断点的地方执行Debugger.Break方案

按照这两点,我们改写了我们的T4模板,在foreach语句之前加上<# Debugger.Break();
#>,并通过<#@import…#>指令导入System.Diagnostics命名空间。我不知道在VS
2008下这种解决方案是否可行,但是如果你使用的是VS
2010,这肯定会导致整个VS的崩溃。当你保存TT文件的时候,如右图所示的对话框弹出来,随之伴随整个VS的Crash。

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"  name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
<#@ import namespace="System.Diagnostics" #>
using System;
public class HelloWord
{
    static void Main()
    {    
        <# Debugger.Break(); #>
        <# foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
        {#>
        Console.WriteLine("Hello, {0}!", "<#=person#>");    
        <# } #>
    }
}

四、在Debugger.Break之前加上Debugger.Launch

为了避免Debugger.Break导致的VS崩溃,只需要在之前多加一句代码即可,既Debugger.Launch。为此我们对我们的T4模板略加修改

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"  name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
<#@ import namespace="System.Diagnostics" #>
using System;
public class HelloWord
{
    static void Main()
    {    
        <# 
        Debugger.Launch();
        Debugger.Break();
        foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
        {#>
            Console.WriteLine("Hello, {0}!", "<#=person#>");    
        <#} #>
    }
}

现在如果你保存该TT文件,VS会弹出如下一个对话框让你选在是否进行Debug。如果需要进行Debug,选择“Yes, debug devenv.exe”。

然后创建一个新的VS实例,或者选择已经打开的VS程序进行Debug,这个对话框我们应该很熟悉。最后程序将会执行到我们设置的断点(Debugger.Break),我们就可以像Debug普通托管程序一样对T4模板进行Debug了。实际上,你也可以直接通过Attach进程的方式进行Debug,不过这里的进程就是VS的进程devenv.exe。

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-05 11:45:44

编写T4模板进行代码生成无法避免的两个话题:"Assembly Locking"&"Debug"的相关文章

一起谈.NET技术,编写T4模板无法避免的两个话题:&amp;quot;Assembly Locking&amp;quot;&amp;amp;&amp;quot;Debug&amp;quot;

在这之前,我写了一系列关于代码生成和T4相关的文章,而我现在也试图将T4引入我们自己的开发框架.在实践中遇到了一些问题,也解决了不少问题.如果你也在进行T4相关的开发,相信你也一定会遇到这些问题.为此,特意将这些问题和解决方案与朋友们分享,希望在遇到这些问题的时候少走弯路.本篇文章介绍的是两个重要的话题:程序集锁定和调试. 目录 一.程序集引用导致的编译问题 二.T4引擎对引用程序集的锁定 三.Debugger.Break导致VS 2010的Crash 四.在Debugger.Break之前加上

编写T4模板无法避免的两个话题:"Assembly Locking"&"Debug"

在这之前,我写了一系列关于代码生成和T4相关的文章,而我现在也试图将T4引入我们自己的开发框架.在实践中遇到了一些问题,也解决了不少问题.如果你也在进行T4相关的开发,相信你也一定会遇到这些问题.为此,特意将这些问题和解决方案与朋友们分享,希望在遇到这些问题的时候少走弯路.本篇文章介绍的是两个重要的话题:程序集锁定和调试. 目录 一.程序集引用导致的编译问题 二.T4引擎对引用程序集的锁定 三.Debugger.Break导致VS 2010的Crash 四.在Debugger.Break之前加上

从零开始编写自己的C#框架(13)——T4模板在逻辑层中的应用(二)

原文:从零开始编写自己的C#框架(13)--T4模板在逻辑层中的应用(二) 最近这段时间特忙,公事私事,忙得有时都没时间打开电脑了,这两周只能尽量更新,以后再将章节补回来.   直接进入主题,通过上一章节,大家明白了怎么使用模板类编写T4模板,本章进的是一些简单技巧的应用 1.首先创建一个Test2.tt模板 2.然后修改模板内容为下面代码 这些代码与上一章最后面的那个差不多,只是修改了输出文件名.命名空间.类名.类属性(partial)和一个单例获取函数 1 <#@ template debu

从零开始编写自己的C#框架(12)——T4模板在逻辑层中的应用(一)(附源码)

原文:从零开始编写自己的C#框架(12)--T4模板在逻辑层中的应用(一)(附源码) 对于T4模板很多朋友都不太熟悉,它在项目开发中,会帮我们减轻很大的工作量,提升我们的开发效率,减少出错概率.所以学好T4模板的应用,对于开发人员来说是非常重要的. 园子里对于T4模板的介绍与资料已经太多了,所以在这里我就不再详细讲述基础知识了,只是说说T4模板在本框架中的具体应用与实践.   一.创建逻辑层项目   二.添加引用 将之前添加的三个项目添加到引用   三.创建T4模板放置的文件夹,并命名为SubS

解决T4模板的程序集引用的五种方案

在众多.NET应用下的代码生成方案中,比如CodeDOM,BuildProvider, 我觉得T4是最好的一种.关于T4的基本概念和模板结果,可以参考我的文章<基于T4的代码生成方式>.如果要了解T4具体的应用,则可以参考我的文章<创建代码生成器可以很简单:如何通过T4模板生成代码?>(上篇)(下篇).如果你编写T4模板,你不得不面对一个问题--如何引用一个程序集?VS 2010采用了与VS2008不同的程序集引用的解析机制.本篇文章为你介绍在VS2010下5种不同的程序集引用的方

一起谈.NET技术,解决T4模板的程序集引用的五种方案

在众多.NET应用下的代码生成方案中,比如CodeDOM,BuildProvider, 我觉得T4是最好的一种.关于T4的基本概念和模板结果,可以参考我的文章<基于T4的代码生成方式>.如果要了解T4具体的应用,则可以参考我的文章<创建代码生成器可以很简单:如何通过T4模板生成代码?>(上篇)(下篇).如果你编写T4模板,你不得不面对一个问题--如何引用一个程序集?VS 2010采用了与VS2008不同的程序集引用的解析机制.本篇文章为你介绍在VS2010下5种不同的程序集引用的方

代码生成新选择-T4模板引擎

在博客堂看到ASP.NET MVC中的T4模板, 之前我也写过一篇文本模板转换工具箱T4. T4(Text Template Transformation Toolkit)则是微软官方在VisualStudio 2008中开始使用的代码生成引擎,可惜T4不像微软公布的别的工具那样参考资料充足,而且模板也很少,MSDN上甚至没有一个专门的目录用来介绍它,惟一沾边的就是在介绍DSL工具时带上的Generating Artifacts By Using Text Templates. Visual W

从零开始编写自己的C#框架(14)——T4模板在逻辑层中的应用(三)

原文:从零开始编写自己的C#框架(14)--T4模板在逻辑层中的应用(三) 原本关于T4模板原想分5个章节详细解说的,不过因为最近比较忙,也不想将整个系列时间拉得太长,所以就将它们整合在一块了,可能会有很多细节没有讲到,希望大家自己对着代码与模板去研究. 本章代码量会比较大,基本将Web层要使用到的大部分函数都用模板生成了出来,而模板中的函数,很多也是互相关联调用的.另外在DotNet.Utilities(公共函数项目)中也添加与修改了一些类和函数. 需要特别说明的是,在逻辑层添加了July大神

创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇]

在<基于T4的代码生成方式>中,我对T4模板的组成结构.语法,以及T4引擎的工作原理进行了大体的介绍,并且编写了一个T4模板实现了如何将一个XML转变成C#代码.为了让由此需求的读者对T4有更深的了解,我们通过T4来做一些更加实际的事情--SQL Generator.在这里,我们可以通过SQL Generator为某个数据表自动生成进行插入.修改和删除的存储过程.[文中源代码从这里下载] 一.代码生成器的最终使用效果 我们首先来看看通过直接适用我们基于T4的SQL生成模板达到的效果.右图(点击