好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

全站式代码生成器示例

全站式代码生成器示例

全站式代码生成器示例

背景

一直在做企业应用,也一直在使用代码生成器,代码生成器分两个维度,一个维度是”主动或被动“,另外一个维度是”运行时或编译时“,这两种维度会有四种组合,每个组合都有其应用的场景,今天我就介绍一下Happy是如何使用代码生成器的。

概念介绍

主动:可以生成多次,会”主动“的合并生成代码和用户自定义代码,C#的部分类和ExtJs的扩展类就是,通过一些文本合并工具也是可以实现的。

被动:不可以生成多次,每次生成都会覆盖用户自定义的代码。

运行时:运行时的代码生成,也叫元编程,动态语言几乎都支持,静态语言可以使用动态编译。

编译时:编译时的代码生成,是狭义的代码生成器的代名词。

还有一点需要说明的,如果是基于应用框架的代码生成器,生成的代码会非常少。

示例

编译时主动+编译时被动代码生成器代码

这里的生成器我是基于NodeJs开发的,T4、CodeSmith和其它代码生成器也不错。

这里的编译时主动是指每次都会生成,用户如果希望个性化代码,就写C#的部分类或ExtJs的扩展类。

这里的编译时被动是指每次都会覆盖用户配置,当然你可以指定只生成一次。

生成后的项目

Application.Generator.cs

  1   using   System;
   2   using   System.Collections.Generic;
   3   using   System.Linq;
   4   using   System.Text;
   5   using   System.Threading.Tasks;
   6                
  7   using   Happy.Command;
   8   using   Happy.Application;
   9   using   Happy.Query.PetaPoco;
  10   using   Demo.Domain;
  11  
 12   namespace   Demo.Application
  13   {
  14  
 15       public   partial   class  TestGridService : AggregateService<IDemoUnitOfWork, ITestGridRepository, TestGrid>  { }
  16      
 17       public   partial   class  TestGridCreateCommand : SimpleCreateCommand<TestGrid>  { }
  18      
 19       public   partial   class  TestGridCreateCommandHandler : SimpleCreateCommandHandler<TestGridService, TestGrid, TestGridCreateCommand>  { }
  20      
 21       public   partial   class  TestGridUpdateCommand : SimpleUpdateCommand<TestGrid>  { }
  22      
 23       public   partial   class  TestGridUpdateCommandHandler : SimpleUpdateCommandHandler<TestGridService, TestGrid, TestGridUpdateCommand>  { }
  24      
 25       public   partial   class  TestGridDeleteCommand : SimpleDeleteCommand<TestGrid>  { }
  26      
 27       public   partial   class  TestGridDeleteCommandHandler : SimpleDeleteCommandHandler<TestGridService, TestGrid, TestGridDeleteCommand>  { }
  28      
 29       public   partial   class   TestGridDynamicQueryService : PetaPocoDynamicQueryService
  30       {
  31           public   TestGridDynamicQueryService() :
  32               base ( @"  Data Source=(LocalDB)\v11.0;AttachDbFilename=  "  + AppDomain.CurrentDomain.BaseDirectory +  @"  App_Data\TestDatabase.mdf;Integrated Security=True;Connect Timeout=30  " ,  "  System.Data.SqlClient  " ,  "  TestGrids  "  )
  33           { }
  34       }
  35  
 36       public   partial   class  TestTreeService : AggregateService<IDemoUnitOfWork, ITestTreeRepository, TestTree>  { }
  37      
 38       public   partial   class  TestTreeCreateCommand : SimpleCreateCommand<TestTree>  { }
  39      
 40       public   partial   class  TestTreeCreateCommandHandler : SimpleCreateCommandHandler<TestTreeService, TestTree, TestTreeCreateCommand>  { }
  41      
 42       public   partial   class  TestTreeUpdateCommand : SimpleUpdateCommand<TestTree>  { }
  43      
 44       public   partial   class  TestTreeUpdateCommandHandler : SimpleUpdateCommandHandler<TestTreeService, TestTree, TestTreeUpdateCommand>  { }
  45      
 46       public   partial   class  TestTreeDeleteCommand : SimpleDeleteCommand<TestTree>  { }
  47      
 48       public   partial   class  TestTreeDeleteCommandHandler : SimpleDeleteCommandHandler<TestTreeService, TestTree, TestTreeDeleteCommand>  { }
  49      
 50       public   partial   class   TestTreeDynamicQueryService : PetaPocoDynamicQueryService
  51       {
  52           public   TestTreeDynamicQueryService() :
  53               base ( @"  Data Source=(LocalDB)\v11.0;AttachDbFilename=  "  + AppDomain.CurrentDomain.BaseDirectory +  @"  App_Data\TestDatabase.mdf;Integrated Security=True;Connect Timeout=30  " ,  "  System.Data.SqlClient  " ,  "  TestTrees  "  )
  54           { }
  55       }
  56  
 57  }

运行时代码生成

目前只做了基于JS的运行时代码生成。

Happy.metadata.Manager.js

   1   /*  *
    2    * 元数据管理器,主要完成元数据的合并和根据元数据生成常用配置和类型。
    3    * 
    4    * @class Manager
    5    * @namespace Happy.metadata
    6    * @constructor
    7    * @param {Object} config
    8    *      @param {Array} config.metadatas 要合并的元数据对象数组,索引越大优先级越高。
    9    */ 
  10  Ext.define('Happy.metadata.Manager' , {
   11       requires: [
   12          'Happy.metadata.DatabaseTypeMapper' ,
   13          'Happy.data.proxy.Ajax'
  14       ],
   15  
  16       /*  *
   17        * 要合并的元数据对象数组,索引越大优先级越高。
   18        * 
   19        * @private
   20        * @property metadatas
   21        * @type Array
   22        */ 
  23  
  24       /*  *
   25        * 合并后的元数据。
   26        * 
   27        * @private
   28        * @property metadata
   29        * @type Object
   30        */ 
  31  
  32       /*  *
   33        * 根据合并后的元数据生成的表单控件配置数组。
   34        * 
   35        * @private
   36        * @property formEditors
   37        * @type Array
   38        */ 
  39  
  40       /*  *
   41        * 根据合并后的元数据生成的表格列配置数组。
   42        * 
   43        * @private
   44        * @property gridColumns
   45        * @type Array
   46        */ 
  47  
  48       /*  *
   49        * 根据合并后的元数据生成的模型类。
   50        * 
   51        * @private
   52        * @property model
   53        * @type Ext.data.Model
   54        */ 
  55  
  56       /*  *
   57        * 根据合并后的元数据生成的仓储类。
   58        * 
   59        * @private
   60        * @property store
   61        * @type Ext.data.Store
   62        */ 
  63  
  64       /*  *
   65        * @method constructor
   66        */ 
  67      constructor:  function   (config) {
   68           var  me =  this  ;
   69  
  70          me.metadatas =  config.metadatas;
   71  
  72           me.initMetadata();
   73  
  74           me.initFormEditors();
   75  
  76           me.initGridColumns();
   77  
  78           if   (me.isTree()) {
   79               me.initTreeColumns();
   80           }
   81  
  82           me.defineModel();
   83           me.defineStore();
   84  
  85           if   (me.isTree()) {
   86               me.defineTreeModel();
   87               me.defineTreeStore();
   88           }
   89       },
   90  
  91       /*  *
   92        * 合并并初始化元数据。
   93        * @private
   94        * @method initMetadata
   95        */ 
  96      initMetadata:  function   () {
   97           var  me =  this  ;
   98  
  99          me.metadata =  {};
  100  
 101          Ext.Array.each(me.metadatas || [],  function   (metadata) {
  102               Ext.merge(me.metadata, metadata);
  103           });
  104       },
  105  
 106       /*  *
  107        * 获取合并后的元数据。
  108        * @method getMetadata
  109        * @return {Object}
  110        */ 
 111      getMetadata:  function   () {
  112           var  me =  this  ;
  113  
 114           return   me.metadata;
  115       },
  116  
 117       /*  *
  118        * @private
  119        * @method getMetadata
  120        * @return {Object}
  121        */ 
 122      isTree:  function   () {
  123           var  me =  this  ;
  124  
 125           return  !!me.metadata.columns['NodePath' ];
  126       },
  127  
 128       /*  *
  129        * 初始化表单控件配置数组。
  130        * @private
  131        * @method initFormEditors
  132        */ 
 133      initFormEditors:  function   () {
  134           var  me =  this  ;
  135  
 136           var  columns =  Ext.Object.getValues(me.metadata.columns);
  137  
 138          me.formEditors = Ext.Array.map(columns,  function   (column) {
  139               var  dataTypeName =  column.dataType.typeName;
  140               var  editorConfig =  me.getDatabaseTypeMapper().getFormEditorConfig(dataTypeName);
  141  
 142               return   Ext.apply({
  143                   name: column.name,
  144                  fieldLabel: column.text ||  column.name
  145               }, editorConfig);
  146           });
  147       },
  148  
 149       /*  *
  150        * 获取表单控件配置数组。
  151        * @method getFormEditors
  152        * @return {Array}
  153        */ 
 154      getFormEditors:  function   () {
  155           var  me =  this  ;
  156  
 157           return   me.formEditors;
  158       },
  159  
 160       /*  *
  161        * 初始化表格列配置数组。
  162        * @private
  163        * @method initGridColumns
  164        */ 
 165      initGridColumns:  function   () {
  166           var  me =  this  ;
  167  
 168           var  columns =  Ext.Object.getValues(me.metadata.columns);
  169  
 170          me.gridColumns = Ext.Array.map(columns,  function   (column) {
  171               var  dataTypeName =  column.dataType.typeName;
  172               var  columnConfig =  me.getDatabaseTypeMapper().getGridColumnConfig(dataTypeName);
  173  
 174               return   Ext.apply({
  175                   dataIndex: column.name,
  176                  text: column.text ||  column.name
  177               }, columnConfig);
  178           });
  179       },
  180  
 181       /*  *
  182        * 获取表格列配置数组。
  183        * @method getGridColumns
  184        * @return {Array}
  185        */ 
 186      getGridColumns:  function   () {
  187           var  me =  this  ;
  188  
 189           return   me.gridColumns;
  190       },
  191  
 192       /*  *
  193        * 获取表单控件配置数组。
  194        * @method getFormEditors
  195        * @return {Array}
  196        */ 
 197      getFormEditors:  function   () {
  198           var  me =  this  ;
  199  
 200           return   me.formEditors;
  201       },
  202  
 203       /*  *
  204        * 初始化树表格列配置数组。
  205        * @private
  206        * @method initTreeColumns
  207        */ 
 208      initTreeColumns:  function   () {
  209           var  me =  this  ;
  210  
 211           var  columns =  Ext.Object.getValues(me.metadata.columns);
  212  
 213          me.treeColumns = Ext.Array.map(columns,  function   (column) {
  214               var  dataTypeName =  column.dataType.typeName;
  215               var  columnConfig =  me.getDatabaseTypeMapper().getGridColumnConfig(dataTypeName);
  216  
 217               return   Ext.apply({
  218                   dataIndex: column.name,
  219                  text: column.text ||  column.name
  220               }, columnConfig);
  221           });
  222       },
  223  
 224       /*  *
  225        * 获取树表格列配置数组。
  226        * @method getTreeColumns
  227        * @return {Array}
  228        */ 
 229      getTreeColumns:  function   () {
  230           var  me =  this  ;
  231  
 232           return   me.treeColumns;
  233       },
  234  
 235       /*  *
  236        * 定义模型类。
  237        * @private
  238        * @method defineModel
  239        */ 
 240      defineModel:  function   () {
  241           var  me =  this  ;
  242  
 243          me.model =  Ext.define(me.getModelName(), {
  244              extend: 'Ext.data.Model' ,
  245               fields: me.getModelFields(),
  246              idProperty: 'Id' ,
  247               proxy: {
  248                  type: 'happy-ajax' ,
  249                   api: {
  250                      create: '/' + me.metadata.singular + 'Command/Create' ,
  251                      read: '/' + me.metadata.singular + 'DynamicQuery/Page' ,
  252                      update: '/' + me.metadata.singular + 'Command/Update' ,
  253                      destroy: '/' + me.metadata.singular + 'Command/Delete'
 254                   },
  255                   reader: {
  256                      type: 'json' ,
  257                      root: 'items' ,
  258                      idProperty: 'Id' ,
  259                      messageProperty: 'message'
 260                   },
  261                   writer: {
  262                      type: 'json' ,
  263                      encode:  true  ,
  264                      root: 'item'
 265                   }
  266               },
  267  
 268              getTableName:  function   () {
  269                   return   me.metadata.name;
  270               }
  271           });
  272       },
  273  
 274       /*  *
  275        * 获取定义的模型类。
  276        * @method getModel
  277        * @return {Ext.data.Model}
  278        */ 
 279      getModel:  function   () {
  280           var  me =  this  ;
  281  
 282           return   me.model;
  283       },
  284  
 285       /*  *
  286        * 定义树模型类。
  287        * @private
  288        * @method defineTreeModel
  289        */ 
 290      defineTreeModel:  function   () {
  291           var  me =  this  ;
  292  
 293           var  fields =  me.getModelFields();
  294  
 295           fields.push({
  296              name: 'parentId' ,
  297              type: 'string' ,
  298              defaultValue:  null  ,
  299              useNull:  false  ,
  300              persist:  false 
 301           });
  302  
 303           fields.push({
  304              name: 'leaf' ,
  305              type: 'bool' ,
  306              defaultValue:  false  ,
  307              persist:  false 
 308           });
  309  
 310          me.treeModel =  Ext.define(me.getTreeModelName(), {
  311              extend: 'Ext.data.Model' ,
  312               fields: fields,
  313              idProperty: 'Id' ,
  314               proxy: {
  315                  type: 'happy-ajax' ,
  316                   api: {
  317                      create: '/' + me.metadata.singular + 'Command/Create' ,
  318                      read: '/' + me.metadata.singular + 'DynamicQuery/Page' ,
  319                      update: '/' + me.metadata.singular + 'Command/Update' ,
  320                      destroy: '/' + me.metadata.singular + 'Command/Delete'
 321                   },
  322                   reader: {
  323                      type: 'json' ,
  324                      root: 'items' ,
  325                      idProperty: 'Id' ,
  326                      messageProperty: 'message'
 327                   },
  328                   writer: {
  329                      type: 'json' ,
  330                      encode:  true  ,
  331                      root: 'item'
 332                   }
  333               },
  334  
 335              getTableName:  function   () {
  336                   return   me.metadata.name;
  337               }
  338           });
  339       },
  340  
 341       /*  *
  342        * 获取定义的树模型类。
  343        * @method getTreeModel
  344        * @return {Ext.data.Model}
  345        */ 
 346      getTreeModel:  function   () {
  347           var  me =  this  ;
  348  
 349           return   me.treeModel;
  350       },
  351  
 352       /*  *
  353        * 获取定义模型类需要的字段数据。
  354        * @private
  355        * @method getModelFields
  356        * @return {Array}
  357        */ 
 358      getModelFields:  function   () {
  359           var  me =  this  ;
  360  
 361           var  columns =  Ext.Object.getValues(me.metadata.columns);
  362  
 363           return  Ext.Array.map(columns,  function   (column) {
  364               var  dataTypeName =  column.dataType.typeName;
  365               var  fieldConfig =  me.getDatabaseTypeMapper().getModelFieldConfig(dataTypeName);
  366  
 367               return   Ext.apply({
  368                   name: column.name
  369               }, fieldConfig);
  370           });
  371       },
  372  
 373       /*  *
  374        * 定义仓储类。
  375        * @private
  376        * @method defineGridStore
  377        */ 
 378      defineStore:  function   () {
  379           var  me =  this  ;
  380  
 381          me.store = Ext.define(me.metadata.namespace + '.' + me.metadata.singular.toLowerCase() + '.store.' +  me.metadata.singular, {
  382              extend: 'Ext.data.Store' ,
  383               model: me.getModelName(),
  384  
 385              getTableName:  function   () {
  386                   return   me.metadata.name;
  387               }
  388           });
  389       },
  390  
 391       /*  *
  392        * 获取定义的仓储类。
  393        * @method getGridStore
  394        * @return {Ext.data.Store}
  395        */ 
 396      getStore:  function   () {
  397           var  me =  this  ;
  398  
 399           return   me.store;
  400       },
  401  
 402       /*  *
  403        * 定义树仓储类。
  404        * @private
  405        * @method defineTreeStore
  406        */ 
 407      defineTreeStore:  function   () {
  408           var  me =  this  ;
  409  
 410          me.treeStore = Ext.define(me.metadata.namespace + '.' + me.metadata.singular.toLowerCase() + '.treestore.' +  me.metadata.singular, {
  411              extend: 'Ext.data.TreeStore' ,
  412  
 413              defaultRootId: '00000000-0000-0000-0000-000000000000' ,
  414               model: me.getTreeModelName(),
  415              root: me.metadata.treeRoot ||  {
  416                   text: me.metadata.singular,
  417                  expanded:  true 
 418               },
  419               proxy: {
  420                  type: 'happy-ajax' ,
  421                   api: {
  422                      create: '/' + me.metadata.singular + 'Command/Create' ,
  423                      read: '/' + me.metadata.singular + 'DynamicQuery/ReadNode' ,
  424                      update: '/' + me.metadata.singular + 'Command/Update' ,
  425                      destroy: '/' + me.metadata.singular + 'Command/Delete'
 426                   },
  427                   reader: {
  428                      type: 'json' ,
  429                      root: 'items' ,
  430                      idProperty: 'Id' ,
  431                      messageProperty: 'message'
 432                   },
  433                   writer: {
  434                      type: 'json' ,
  435                      encode:  true  ,
  436                      root: 'item'
 437                   }
  438               },
  439  
 440              getTableName:  function   () {
  441                   return   me.metadata.name;
  442               }
  443           });
  444       },
  445  
 446       /*  *
  447        * 获取定义的树仓储类。
  448        * @method getTreeStore
  449        * @return {Ext.data.Store}
  450        */ 
 451      getTreeStore:  function   () {
  452           var  me =  this  ;
  453  
 454           return   me.treeStore;
  455       },
  456  
 457       /*  *
  458        * 获取定义模型类需要的类名。
  459        * @private
  460        * @method getModelName
  461        * @return {String}
  462        */ 
 463      getModelName:  function   () {
  464           var  me =  this  ;
  465  
 466           return  me.metadata.namespace + '.' + me.metadata.singular.toLowerCase() + '.model.' +  me.metadata.singular;
  467       },
  468  
 469       /*  *
  470        * 获取定义树模型类需要的类名。
  471        * @private
  472        * @method getTreeModelName
  473        * @return {String}
  474        */ 
 475      getTreeModelName:  function   () {
  476           var  me =  this  ;
  477  
 478           return  me.metadata.namespace + '.' + me.metadata.singular.toLowerCase() + '.treemodel.' +  me.metadata.singular;
  479       },
  480  
 481       /*  *
  482        * 获取数据库类型映射器。
  483        * @private
  484        * @method getModelName
  485        * @return {Happy.metadata.DatabaseTypeMapper}
  486        */ 
 487      getDatabaseTypeMapper:  function   () {
  488           var  me =  this  ;
  489  
 490           return   Happy.metadata.DatabaseTypeMapper;
  491       }
  492  });

运行效果

备注

刚开了个头,这篇文章只是介绍了代码生成器的一些使用场景,但是真正重要的是系统的架构风格,我的偏好是DDD + CQRS,因此最终的目标是支持DDD + CQRS,代码生成器只不过帮我写了一些代码,如果要做到支持DDD + CQRS的话,需要抽象出很多元数据,比如:聚合根、实体、值对象和他们的关系等等。

 

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于全站式代码生成器示例的详细内容...

  阅读:42次