• Template Studio 模板开发入门 Lpt templates Development


    作为一款优秀的ORM工具,一直享受着LLBL Gen快速代码生成的好处,却很少钻研它的原理,趁项目不忙,研究下LLBL Gen 3.x的模板开发方法。如果不熟悉基础的配置和步骤,请参考文章《优秀的基于模板的代码生成工具Template Studio》。LLBL Gen提供的ORM框架是免费的,源代码也可以从官网获取,ORM设计器需要商业许可。

    既然是入门,肯定不能太复杂,参考Code Smith的入门资料的例子,生成下面的文件,作为模板代码生成的入门例子。

    using System;
    namespace <%=NameSpace %>
    {
         /// <summary>
         /// Summary description for <%=ClassName %>.
         /// </summary>
          public class <%=ClassName %>
          {
                public <%=ClassName %>()
                {
                     //
                     // TODO: Add constructor logic here
                     //
                }
          }

    }

    打开LLBL Gen的设计器,加载一个项目,点击菜单Windows->Show Template Bindings Viewer,调出Template Studio。注意,LLBL Gen 2.x的模板工具是独立的程序,称作Template Studio,LLBL Gen 3.x将模板工具直接集成到LLBL Gen设计器中,方便使用。新建立一个Template Bindings,界面如下图所示

    image

    添加两个模板Entity和EntityProperty,放在ISLTemplate目录下面,书写模板和模板生成的代码的语言都是C#。

    CTRL+E,编辑模板Entity,因为是新增加的模板,会提示是否创建模板文件,选择Yes,Template Sutdio会创建模板文件,并打开它,供编辑模板,请添加如下的模板代码

    <%
    Project currentProject = _executingGenerator.ProjectDefinition;
    EntityDefinition currentEntity = (EntityDefinition)_activeObject;

    %>
    using System;
    using System.Data;
    using <%=_executingGenerator.RootNamespaceToUse%>;

    namespace <%=_executingGenerator.RootNamespaceToUse%>
    {
          /// <summary>
          /// Summary description for <%=currentEntity.Name%>.
          /// </summary>
          public class <%=currentEntity.Name%>
          {
                public <%=currentEntity.Name%>()
                {
                      //
                      // TODO: Add constructor logic here
                      //
                }
          }
    }

    如果有写过T4或是Code Smith模板的经验,这些经验仍然可以派上用场。不同的模板生成工具,不同的方面是获取元数据的方法,先在Visual Studio中写好最终需要的代码,然后把变化的部分用元数据替换。

    <% 和 %> 包括的部分是代码片段,在运行时会执行。_executingGenerator是Template Studio内置的变量,在运行时动态传入值,它的类型是IGenerator,定义于程序集SD.LLBLGen.Pro.GeneratorCore中。还有两个变量

    _parameters, 类型是Dictionary(String, TaskParameter) ,存放当前task的参数

    _activeObject, 如果emitType=allEntites,则它代表当前的实体对象

    这样解释感觉不太明朗,没有说透。以Entity模板为例子,模板引擎会为它生成一个类型,代码如下

    public class Entity : ITemplateClass {
        private StreamWriter __outputWriter;
        private IGenerator _executingGenerator;
        private Dictionary<string, TaskParameter> _parameters;
        private object _activeObject;

        public Entity() {
            __outputWriter=null;_executingGenerator=null;_parameters=null;_activeObject=null;
        }

        private void __ScriptCode() {

    Project currentProject = _executingGenerator.ProjectDefinition;
    EntityDefinition currentEntity = (EntityDefinition)_activeObject;

           __outputWriter.Write("\r\nusing System;\r\nusing System.Data;\r\n");
           __outputWriter.Write("using ");
           __outputWriter.Write(_executingGenerator.RootNamespaceToUse);
           __outputWriter.Write(";\r\n\r\nnamespace ");
           __outputWriter.Write(_executingGenerator.RootNamespaceToUse);
           __outputWriter.Write("\r\n{\r\n      /// <summary>\r\n      /// Summary description for ");
           __outputWriter.Write(currentEntity.Name);
           __outputWriter.Write(".\r\n      /// </summary>\r\n      public class ");
           __outputWriter.Write(currentEntity.Name);
           __outputWriter.Write("\r\n      {\r\n            public ");
           __outputWriter.Write(currentEntity.Name);
           __outputWriter.Write("()\r\n            {\r\n     //\r\n   // TODO: Add constructor logichere\r\n           //\r\n            }\r\n            \r\n");
            __outputWriter.Write("\r\n\r\n           ");
            __outputWriter.Write("\r\n\r\n");
            __outputWriter.Write("\r\n//");
            __outputWriter.Write("\r\n\r\n\r\n");
            __outputWriter.Write("\r\n      }\r\n}");
        }

        public void ___RUN(IGenerator executingGenerator, Dictionary<string, TaskParameter> parameters,   StreamWriter outputWriter, object activeObject) {
            __outputWriter = outputWriter; 
            _parameters = parameters;   
            _executingGenerator=executingGenerator;
            _activeObject = activeObject;
            __ScriptCode();
        }

    }

    模板引擎会运行生成的___RUN方法,传放当前的项目信息(executingGenerator包含项目信息),和task的参数,如果是多次执行,将当前的对象赋给activeObject ,一并传入lpt模板。模板引擎分析lpt模板时,如果是字符串则直接用变量__outputWriter输出,如果是变量,比如<%=currentEntity.Name%>,则取它的值。

    做基于LLBL Gen的代码生成器,所需要元数据就都在以上这四个变量中,如果不熟悉它的属性,可以用Reflector查看。

    Entity模板设计好后,还需要设计它的传入参数。因为是自定义的模板,点击Add Tasks,添加ConsumeLptTemplate

    image

    再来配置它的几个参数。destinationFolder是存放模板的路径,相对于在General settings中设定的Destination root目录,可以使用DOS命令中的..和./表示当前目录或上一层目录,也可以放空,表示Destination root目录。如果它的值不为空,比如为GUI或是默认的值FolderName,则要添加一个DirectoryCreator任务,先产生目录,再来生成代码。
    设置emitType=allEntities,因为这里是要为所有的实体生成一个类型文件,如果只为部分实体生成类型文件,请依照下图,来勾选需要生成代码的类型。

    emitType的其它几个值,allTypedLists是所有的类型列表,allTypedViews所有视图,actionSPCalls所有命令存储过程,retrievalSPCalls所有查询存储过程,generic只执行一次

    templateID设置为Entity,以表示当前任务,要使用的模板名称。filenameFormat默认为[elementName].[extension],可不作修改,elementName是实体,extension依据当前要生成代码的项目类型,可以是VB或CS。

    其它的选项可不作修改,最终的配置效果如图所示

    image

    点击 Start generator,就会在Destination root目录中生成代码,对每一个实体,生成一个类型文件。

    前面已经提到过,要加入一些代码,作出逻辑判断,可以用<%和%>包括起来,这种情形用于修改元数据的值。

    和ASP.NET相似,<%和%>括起工代码片段,,<%=%>,中间的部分是表达式,返回字符串

    比如元数据提供的类型名称是Employee,而我需要的字符串是IEmployee,则可以用这种方法
    public class I<%=currentEntity.Name%>  {             }

    C#或VB代码片段的例子如下

    <%
    for(int i=0;i<_executingGenerator.ProjectDefinition.Entities.Count;i++)
    {
           %>  some text which will end up Count times in the output <%
    }
    %>


    如果做过LLBL Gen的开发,对__LLBLGENPRO_USER_CODE_REGION不会陌生。代码生成器在重新生成代码时,会保留这个标记符号中间的代码,而不会出现代码丢失。所以,要修改代码生成器生成的代码,增加一些业务逻辑,也应该遵守这个规则,把代码放到这两个符号级中间

    // __LLBLGENPRO_USER_CODE_REGION_START Entity Property 
    // __LLBLGENPRO_USER_CODE_REGION_END

    要生成这种符号标记,应该用这样的代码
    <%=DotNetTemplateEngine.GetUserCodeRegion("Entity Property", "//")%>

    LLBL Gen的模板允许嵌套,也就是一个模板文件中,可以嵌套定义多个子模板。以开头的两个模板Entity和EntityProperty模板为例子,要将EntityProperty模板嵌入到Entity模板中,应该这样写

    <# EntityProperty #>

    这令我想起了ASP和ASP.NET时代的页面嵌套,很相似。

    使用<%和%>包括起来的代码片段部分,也可以包含方法定义,然后再在合适的地方,调用方法,请参考如下例子

    <~
    public string GetObjectName()
    {
        if(_activeObject==null)
        {
            return "no active object";
        }
        EntityDefinition e = _activeObject as EntityDefinition;
        if(e!=null)
        {
            return e.Name;
        }
        TypedListDefinition tl = _activeObject as TypedListDefinition;
        if(tl!=null)
        {
            return tl.Name;
        }
        TypedViewDefinition tv = _activeObject as TypedViewDefinition;
        if(tv!=null)
        {
            return tv.Name;
        }
        SPCallDefinition sc = _activeObject as SPCallDefinition;
        if(sc!=null)
        {
            return sc.Name;
        }
        // unknown object
        return "Unknown";
    }
    ~>

    //<%=GetObjectName()%>

    如果方法定义于一个外部程序集中,可以用这种方法引用外部程序集,用<[和]>括起来,例子如下

    <[ System.Data.SqlClient ]>


    如果要输出ASP.NET的类型变量,比如输出Foo,用C#是这样写:Response.Write("Foo");
    转换成LLBL Gen模板语法:<%%=Response.Write("Foo");%%>

    如果需要调试模板,参数设置debugBuild=true,在模板的开头添加<[ System.Diagnostics ]>,然后在需要停下来的地方,添加代码<% //  Debugger.Break(); %>,这样在运行模板时,生调出窗口,选择调试器,以让调试器跟踪到生成的代码中去。不过,这种方法测试过多次,也没有成功调试。

    LLBL Gen的模板编辑器,没有智能提示功能,只是个语法高亮的文本编辑器,LLBL Gen 3.x也没有提供对模板的编译和语法检查,2.x有语法检查功能,确实不好用。我想到的办法是,用Visual Studio来写模板代码,然后拷贝到Template Bindings Viewer编辑器中,对于错误模板的诊断和分析,只有靠经验来判断,这会令我无限的怀恋Visual Studio的功能,边写代码,后台自动检测语法错误。也许这是一个契机,开发一款灵活好用的LLBL Gen的模板编辑器

  • 相关阅读:
    javascript 与 asp.net 的交互
    10 Mar 10 Auto Complete Ajax Scripts
    Asp.net性能优化总结
    window.print(); is not working in Safari Browser Windows XP
    用VS.NET中的测试工具(Application Center Test)测试ASP.NET程序
    ASP.NET页面事件过程多个用户控件时的执行过程
    Swt/Jface treeViewer的使用
    批量引入struts标签简化引入操作
    android学习笔记47android_手势识别技术的实现,手势库的创建
    学校举行的ACM,有道题没弄明白!!
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/2161544.html
Copyright © 2020-2023  润新知