zoukankan      html  css  js  c++  java
  • 幸福框架:全站式代码生成器示例

    背景

    一直在做企业应用,也一直在使用代码生成器,代码生成器分两个维度,一个维度是”主动或被动“,另外一个维度是”运行时或编译时“,这两种维度会有四种组合,每个组合都有其应用的场景,今天我就介绍一下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的话,需要抽象出很多元数据,比如:聚合根、实体、值对象和他们的关系等等。

  • 相关阅读:
    AOP-面向切面编程-1
    记一次付工解决Sqlserver问题的过程
    Mysql ---Sqlserver数据迁移到Mysql(Mysql建表迁移数据)
    Mysql ---部署,创建用户
    【C++】C++未定义行为
    【C++】回看面向对象与C++
    【作业】2017级面向对象程序设计——总结作业
    【笔记】Cocos2dx学习笔记
    【个人】绝地求生—吃豆人
    【团队】汇总博客
  • 原文地址:https://www.cnblogs.com/happyframework/p/3116390.html
Copyright © 2011-2022 走看看