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的模板编辑器

一、使用对象关系映射组件Kenly.DBFramework.dll不用编写任何SQL或者存储过程即可实现下列功能: 1、数据表、视图和存储过程与对象之间的转换。 2、数据表、视图的自定义条件查询。 3、数据表、视图的分页查询。 4、根据ID、主键或自定义条件对数据表进行增、删、改操作。 5、实现一对一、一对多、多对一和多对多的关系映射。 6、支持单个对象和多个对象之间的事务控制。 7、支持查询结果排序。 8、支持查询表达式生成。 9、支持延迟加载。 二、代码生成器 1、根据指定的数据库连接,自动生成数据表、视图和存储过程对应的对象代码(C#代码)。 2、自动生成相关的工程文件,生成完整的业务层项目文件。 3、可以帮助生成自定义查询方法。 4、支持SQLServer2000、SQLServer2005和Oracle代码生成插件,支持插件扩展。 提供 1、对象关系映射组件: Kenly.DBFramework.dll 2、代码生成器源码:Xsd2CS。 3、代码生成器插件源码(支持SQLServer2000、SQLServer2005和Oracle):Plugin。 4、使用手册:DBFramework.Manual V4.5.3.pdf。 主要API: public abstract class ViewGateway<T> where T: new() { // Methods static ViewGateway(); protected ViewGateway(); protected static int Count(); protected static int Count(string condition); protected static List<int> CountGroup(string groupBy); protected static List<int> CountGroup(string condition, string groupBy); public static List<T> CreateInstances(int count); protected static List<ColumnType> Distinct<ColumnType>(string columnName); protected static List<ColumnType> Distinct<ColumnType>(string columnName, string condition); protected static List<ColumnType> Distinct<ColumnType>(string columnName, string condition, bool ignoreNull); protected static bool Exists(string condition); public bool ExistsById(); public bool ExistsByPK(); protected static List<T> Find(PagingArg pagingArg, params string[] propertyNames); protected static List<T> Find(string condition, params string[] propertyNames); protected static List<T> Find(PagingArg pagingArg, bool sqlServer2000, params string[] propertyNames); protected static List<T> Find(string condition, PagingArg pagingArg, params string[] propertyNames); protected static List<T> Find(string condition, PagingArg pagingArg, bool sqlServer2000, params string[] propertyNames); protected static List<T> FindAll(params string[] propertyNames); protected static void FindTop(T firstOne); protected static List<T> FindTop(int topCount, params string[] propertyNames); protected static void FindTop(string condition, T firstOne); protected static List<T> FindTop(string condition, int topCount, params string[] propertyNames); protected static void InitializeGateway(GatewayConfig config); protected static void InitializeGateway(DatabaseType dbType, string connectionString); protected void InitializeInstance(T entity); protected static object Max(string columnName); protected static object Max(string columnName, string condition); protected static List<object> MaxGroup(string columnName, string groupBy); protected static List<object> MaxGroup(string columnName, string condition, string groupBy); protected static object Min(string columnName); protected static object Min(string columnName, string condition); protected static List<object> MinGroup(string columnName, string groupBy); protected static List<object> MinGroup(string columnName, string condition, string groupBy); protected static DataTable Query(PagingArg pagingArg, params string[] propertyNames); protected static DataTable Query(string condition, params string[] propertyNames); protected static DataTable Query(PagingArg pagingArg, bool sqlServer2000, params string[] propertyNames); protected static DataTable Query(string condition, PagingArg pagingArg, params string[] propertyNames); protected static DataTable Query(string condition, PagingArg pagingArg, bool sqlServer2000, params string[] propertyNames); protected static DataTable QueryAll(params string[] propertyNames); protected static DataTable QueryTop(int topCount, params string[] propertyNames); protected static DataTable QueryTop(string condition, int topCount, params string[] propertyNames); public ArrayList RetrieveAssociations(); public ArrayList RetrieveAssociations(Type elementType); public ArrayList RetrieveAssociations(params Type[] elementTypes); public void RetrieveById(); public void RetrieveByPK(); protected internal virtual void RetrieveBySql(string sql); public void ShadowCopyTo(object targetEntity); public void ShadowCopyTo(T targetEntity); protected static double Sum(string columnName); protected static double Sum(string columnName, string condition); protected static List<double> SumGroup(string columnName, string groupBy); protected static List<double> SumGroup(string columnName, string condition, string groupBy); public static string ToColumnName(string propertyName); public static T ToObject(DataRow adaptedRow); public static List<T> ToObjects(DataTable adaptedTable); public static List<T> ToObjects(DataRow[] adaptedRows); public static string ToPropertyName(string columnName); public static DataTable ToTable(IList<T> entities); public static DataTable ToTable(params T[] entities); public static DataTable ToTable(bool isAdapted, params T[] entities); public static DataTable ToTable(bool isAdapted, IList<T> entities); // Properties protected internal static string CommandText { get; } protected internal static GatewayConfig Config { get; } internal T Entity { get; set; } public object EntityId { get; } public object PrimaryKey { get; } protected static Order SortedOrder { get; set; } } public abstract class TableGateway<T> : ViewGateway<T> where T: TableGateway<T>, new() { // Methods static TableGateway(); protected TableGateway(); public int AddNew(); public static int AddNew(IList<T> entities); public static int AddNew(T entity); public int AddNew(bool returnIdentity); public static int AddNew(T entity, bool returnIdentity); public static int AddNew(IList<T> entities, out Transaction transaction); public static int AddNew(IList<T> entities, bool returnIdentity); public static int AddNew(T entity, out Transaction transaction); public static int AddNew(IList<T> entities, bool returnIdentity, out Transaction transaction); public static int AddNew(T entity, bool returnIdentity, out Transaction transaction); public IDbTransaction BeginTransaction(); public IDbTransaction BeginTransaction(IsolationLevel isolationLevel); public IDbTransaction BeginTransaction(double activeTime); public IDbTransaction BeginTransaction(IsolationLevel isolationLevel, double activeTime); public bool Commit(); protected static int Delete(string condition); protected static int Delete(string condition, out Transaction transaction); public int DeleteById(); public static int DeleteById(T entity); public static int DeleteById(IList<T> entities); public static int DeleteById(IList<T> entities, out Transaction transaction); public static int DeleteById(T entity, out Transaction transaction); public int DeleteByPK(); public static int DeleteByPK(T entity); public static int DeleteByPK(IList<T> entities); public static int DeleteByPK(IList<T> entities, out Transaction transaction); public static int DeleteByPK(T entity, out Transaction transaction); protected void OnPropertyChanged(MethodBase propertyMethod); protected void OnPropertyChanged(string propertyName); protected internal override void RetrieveBySql(string sql); public bool Rollback(); protected static int Update(T entity, string condition); protected static int Update(T entity, string condition, out Transaction transaction); public int UpdateById(); public static int UpdateById(IList<T> entities); public static int UpdateById(T entity); public static int UpdateById(T entity, out Transaction transaction); public static int UpdateById(IList<T> entities, out Transaction transaction); public int UpdateByPK(); public static int UpdateByPK(IList<T> entities); public static int UpdateByPK(T entity); public static int UpdateByPK(IList<T> entities, out Transaction transaction); public static int UpdateByPK(T entity, out Transaction transaction); // Properties public bool Changed { get; } public List<string> ChangedPropertyNames { get; } } public class StoredProcedure<T> : IStoredProcedure { public bool BeginTransaction(); public void CloseReader(IDataReader reader); public bool Commit(); public DataSet ExecuteDataSet(); public DataTable ExecuteDataTable(); public DataTable ExecuteDataTable<Ttarget>(); public List<Ttarget> ExecuteEntity<Ttarget>(); public int ExecuteNonQuery(); public IDataReader ExecuteReader(); public object ExecuteScalar(); protected static void InitializeGateway(GatewayConfig config); protected static void InitializeGateway(DatabaseType dbType, string connectionString); protected void InitializeInstance(T entity); public bool Rollback(); } public class AggregateEntity<T> where T: AggregateEntity<T>, new() { public static List<T> Execute(); protected static List<T> Execute(string condition); public static DataTable ExecuteDataTable(); protected static DataTable ExecuteDataTable(string condition); protected static void InitializeGateway(GatewayConfig config); protected static void InitializeGateway(DatabaseType dbType, string connectionString); // Properties protected static Order SortedOrder { get; set; } } public static class EntityMapper<T> { // Methods public static void AdaptToDatabase(DataTable adaptedTable); public static void AdaptToEntity(DataTable rawTable); public static void CopyToEntities(IList<T> entities, IList<DataRow> adaptedRows); public static void CopyToEntities(IList<T> entities, DataTable adaptedTable); public static void CopyToEntity(T entity, DataRow row); public static List<T> CreateEntities(int count); public static DataTable CreateTable(); public static DataTable CreateTable(bool isAdapted); public static string ToColumnName(string propertyName); public static List<T> ToEntities(IList<DataRow> adaptedRows); public static List<T> ToEntities(DataTable table); public static T ToEntity(DataRow adaptedRow); public static string ToPropertyName(string columnName); public static DataTable ToTable(IList<T> entities); public static DataTable ToTable(params T[] entities); public static DataTable ToTable(bool isAdapted, params T[] entities); public static DataTable ToTable(bool isAdapted, IList<T> entities); } public static class EntityUtility { // Methods public static List<T> Inherit<T>(IList entities); public static T Inherit<T>(object entity); public static T Inherit<Tbase, T>(Tbase baseEntity, TransformAction<Tbase, T> method); public static List<T> Inherit<Tbase, T>(IList<Tbase> baseEntities, TransformAction<Tbase, T> method); public static void ShadowCopy(IList targetEntities, IList sourceEntities); public static void ShadowCopy(object targetEntity, object sourceEntity); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值