X-Admin&ABP框架开发-代码生成器

  在日常开发中,有时会遇到一些相似的代码,甚至是只要CV一次,改几个名称,就可以实现功能了,而且总归起来,都可以由一些公用的页面更改而来,因此,结合我日常开发中使用到的页面,封装一个适合自己的代码生成器,仅处于入门阶段,包括生成的代码结构都仅是把框架展示出来,内部详细暂时没得,针对于应用服务中的接口和实现,相关Dto,MVC中的控制器、视图及视图模型进行了模板制作及生成相关的文件。

一、设计思路

  方案一:开始想到的是,搞个控制台,然后给一个.cs文件,然后控制台去解析其中的命名空间,类名,属性,再用配置好的razor模板去替换,再生成相关的一些文件出来,但是发现,万事开头难,第一步去解析cs文件就不好搞,找了网上的资料,不太好弄,干脆想了下,放弃这种方案,因为想到了另一种常用的方案。

  方案二:直接在控制台中,配置控制台去访问数据库,然后给定指定表名,去读取数据库中的表和字段,再反过来去生成相关文件,但是这里会遇到一个这样的问题,比如我使用的是mysql,mysql本身有个配置表名大小写忽视的,这样一来,获取到的表名都将是小写打头,尽管可能配置了是区分大小写,但是,我设计表时,采用Pre_table,形式区分业务表,比如是CRM模块需要用到的CRM_Client,那将用CRM打头,后面这部分Client才是实际代码中的类名,种种问题都有可能,但是作为没有那么多可能性下,比如没得前缀,不区分大小写,形式简单,那么可以考虑使用。此时,想到了abp中的Migrator控制台并想到了方案三。

  方案三:如果说直接搞一个控制台在代码中,模仿Abp自带的Migrator一样,启动后,给定类名,通过反射去取得该类的属性名,岂不是美滋滋,需要哪个类的相关文件,只需启动,然后输入类名,即可得到相关的文件。这几种方案的前提都是在Dto文件中会展示所有实体字段,如果需要选择性的使用字段,则还需借助人工智能,以人力去完成更改生成的文件。

二、Razor引擎的使用

  我选择了方案二作为入手去实现,并且采用Razor引擎作为模板解析的工具。Nuget引入RazorEngine.NetCore包,开始实现依靠模板生成代码。

1、先尝试下Razor引擎,控制台中CV下Razor引擎提供的Demo,引入相关命名空间,学习下如何去使用。

string template = "Hello @Model.Name, welcome to RazorEngine!";
var result = Engine.Razor.RunCompile(template, "templateKey", null, new { Name = "World" });
Console.WriteLine(result);

运行完毕,可以获取到运行结果,需要注意的是,如果是在linux或是mac跑会得到错误,该问题是Razor引擎本身的问题,暂时只能在window下跑。

2、熟悉了下Razor的使用方式后,开始使用简单文件形式填充一些数据模拟生成过程。

首先,一个文件作为填充模板,一个文件内存储Json数据作为数据源,程序启动时加载两个文件。

var templatePage = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SimpleCOders\\Templates", "templatePage.txt");
TemplatePage = File.ReadAllText(templatePage);

var templatePageJson = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SimpleCOders\\Templates", "templatePageJson.json");
TemplatePageJson = File.ReadAllText(templatePageJson);

其次,数据源整理成相应类结构,得到批量待解析数据。

var templatePageJsonList = JsonConvert.DeserializeObject<List<PageDataModel>>(TemplatePageJson);

foreach (var templatePageJson in templatePageJsonList)
{
    RazorParse(
        templatePageJson.Index ?? 1,
        templatePageJson.Date,
        templatePageJson.Index - 1,
        templatePageJson.Index + 1,
        templatePageJson.Content
    );
}

最后,设计一下解析器,将读取到的数据源,进行解析成相关的类,然后依次按照模板生成文件

var entityResult = Engine.Razor.RunCompile(TemplatePage, "templatePageKey", null, new
{
    PostData = (date ?? DateTime.Now).ToString("yyyy-MM-dd"),
    PrevIndex = prev.Value,
    NextIndex = next.Value,
    ContentHtml = content
});

按照一条数据便是一个模板文件去生成可以得到批量生成文件。

三、适合自己的简单代码生成器

  开始着手适合自己的简单代码生成器,思路一致,只是增加了需要读取数据库这一环节。

1、模板制作,以应用服务接口为例,常用的增删改查进行封装,利用Razor语法进行填充处理,此处对于主键类型,没有进行处理,只能支持诸如int、long之类的,后期在继续优化。

using Abp.Application.Services;
using Abp.Application.Services.Dto;
using System.Collections.Generic;
using System.Threading.Tasks;
using @[email protected]@(Model.EntityName)s.Dto;

namespace @[email protected]@(Model.EntityName)s
{
    /// <summary>
    /// @(Model.EntityDescription)应用服务接口
    /// </summary>
    public interface [email protected](Model.EntityName)AppService : IApplicationService
    {
        /// <summary>
        /// 获取@(Model.EntityDescription)数据集合
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<PagedResultDto<@(Model.EntityName)ListDto>> [email protected](Model.EntityName)([email protected](Model.EntityName)Input input);

        /// <summary>
        /// 获取@(Model.EntityDescription)编辑信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<[email protected](Model.EntityName)ForEditOutput> [email protected](Model.EntityName)ForEdit(NullableIdDto<@Model.EntityKeyType> input);

        /// <summary>
        /// 创建或修改@(Model.EntityDescription)信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task [email protected](Model.EntityName)([email protected](Model.EntityName)Input input);

        /// <summary>
        /// 删除@(Model.EntityDescription)
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task [email protected](Model.EntityName)(List<EntityDto<@Model.EntityKeyType>> inputs);
    }
}

2、设置相应的解析器,与之前的尝试不同,这次使用了具体的类型,这是Razor中的另一种方式,解析完毕后将文件按照指定路径保存,尽量符合项目的路径存储。

var iRazorAppService = Engine.Razor.RunCompile(IRazorAppService, nameof(IRazorAppService), typeof(TemplateParseModel), templateParseModel);
UtilHelper.Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, applicationPath, $"I{templateParseModel.EntityName}AppService.cs"), iRazorAppService);
builder.Append(iRazorAppService);

3、数据库连接读取表结构,控制台下,采用直接读取的形式,不走DbContext方式,Nuget中引入MySql.Data包(我本地用的Mysql),增加Appsettings.json文件并配置好连接字符串,用sql语句形式直接读取数据库中的信息,此处封装了一个DbHelper类及将读取到的信息封装到指定类中。

using (var SqlConnection = new MySqlConnection(connectionStr))
{
    SqlConnection.Open();
    var columsInfo = string.Format(@"select table_name,column_name,ordinal_position,is_nullable,data_type,character_maximum_length,column_key,column_comment
        from information_schema.COLUMNS
        where table_schema = ‘{0}‘ and table_name = ‘{1}‘", dbschema, tablename);

    MySqlCommand mySqlCommand = new MySqlCommand(columsInfo, SqlConnection);
    MySqlDataReader dataReader = mySqlCommand.ExecuteReader();

    List<ColumnInfo> sqlDatasList = new List<ColumnInfo>();
    while (dataReader.Read())
    {
        var columnInfo = new ColumnInfo()
        {
            TableName = dataReader[dataReader.GetName(0)].ToString(),
            Name = dataReader[dataReader.GetName(1)].ToString(),
            OrdinalPosition = StringExtension.GetValueOrNull<int>(dataReader[dataReader.GetName(2)].ToString()),
            IsNullable = dataReader[dataReader.GetName(3)].ToString(),
            DataType = dataReader[dataReader.GetName(4)].ToString(),
            CharacterMaximumLength = StringExtension.GetValueOrNull<int>(dataReader[dataReader.GetName(2)].ToString()),
            ColumnKey = dataReader[dataReader.GetName(6)].ToString(),
            ColumnComment = dataReader[dataReader.GetName(7)].ToString(),
        };
        sqlDatasList.Add(columnInfo);
    }

    dataReader.Close();
    SqlConnection.Close();
    return sqlDatasList;

4、启动后输入表名、实体名、实体描述(并未保存到数据库中),再通过手动将其加入到项目中,诸如命名空间及模块名称都加入到了配置文件中,方便配置,至少相对手动去一个个添加来讲,减少了部分工作量,也达到了辅助的效果,但是要达到全面辅助,还得在进行继续优化,针对其中的类等等,暂时没有加入属性,只放置了Id、Name等等,之后得考虑把数据库中字段也循环输出到模板文件中。

 至此,依靠Razor引擎制作一个简单的(算是减少了工作量)代码生成器初步完成了,年后继续完善,加入丰富的功能,并移入到框架中作为提高生产力的手段。新年快乐~

 仓库地址:https://gitee.com/530521314/Partner.TreasureChest.git

2020-01-01,望技术有成后能回来看见自己的脚步

原文地址:https://www.cnblogs.com/CKExp/p/11996382.html

时间: 2024-08-29 22:42:49

X-Admin&ABP框架开发-代码生成器的相关文章

X-Admin&amp;ABP框架开发-版本管理

多租户系统中,针对于不同租户开放不同功能,或是按照不同功能进行收费管理,需要从宿主本身去管理租户的版本信息,如同酒店人员对不同房间收取不同费用,依据房间内部设施,房间大小等设置不同收费标准.Abp系统中默认是多租户的,并且在Zero模块中实现了版本管理功能. 演示地址:http://119.3.138.127/,更改Account/HostLogin进入宿主管理 一.设计前提 基于Abp进行了相关限制,我将多租户变成了单租户,不允许添加新的租户,由于日常接触中,发现除了云平台这种SaaS需要多租

X-Admin&amp;ABP框架开发-设置管理

在网站开发中,设置是不可缺少的一环,如用户设置.系统设置.甚至是租户设置等.ABP对于设置的管理已经做了很好的处理,我们可以借助巨人的力量来完成我们的冒险. ABP官网地址:https://aspnetboilerplate.com/ 一.设置的层级划分 ABP中提供了三种类型的设置,用户级别.应用级别.租户级别,针对于不同级别有着不同的侧重点,比如用户级别,针对于用户的一些设置,如主题设置,接收通知设置等:针对应用级别,该级别也能在用户层级上进行影响,好比如设置统一的主题皮肤,而它主要体现在整

Module Zero模块 [X-Admin&amp;ABP框架开发-RBAC]

在业务系统需求规划过程中,通常对于诸如组织机构.用户和角色等这种基础功能,通常是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来讲,是系统的基石,整个业务体系是建立于这部分基石之上的,当然,还有诸如多语言.设置管理.认证和授权等.对于这部分功能,ABP中存在这些概念,并且通过Module Zero模块完成了这些概念. 一.角色访问控制之RBAC RBAC:Role Based Access Control,基于角色的访问控制,这在目前大多数软件中来讲已经算得上是普遍应用了,最常见的结构

ABP框架个人开发实战(1)_环境搭建

前言 之前关注ABP框架有一阵子了,一直没有潜下心来实际研究一下.最近想自己建站,以后有自己的功能开发项目,可以在自己的站点上开发,并一步步的完善,所以找个比较好用的框架迫在眉睫,选来选去,决定用ABP框架.用群里的大大门的话来说,掌握了ABP,基本就可以飞天了~ 先简单介绍下吧(以下部分资料来自群里资料,如有侵权,请告知): ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开

ABP开发框架前后端开发系列---(15)ABP框架的服务端和客户端缓存的使用

缓存在一个大型一点的系统里面是必然会涉及到的,合理的使用缓存能够给我们的系统带来更高的响应速度.由于数据提供服务涉及到数据库的相关操作,如果客户端的并发数量超过一定的数量,那么数据库的请求处理则以爆发式增长,如果数据库服务器无法快速处理这些并发请求,那么将会增加客户端的请求时间,严重者可能导致数据库服务或者应用服务直接瘫痪.缓存方案就是为这个而诞生,随着缓存的引入,可以把数据库的IO耗时操作,转换为内存数据的快速响应操作,或者把整个页面缓存到缓存系统里面.本篇随笔主要介绍利用ABP框架的支持实现

2019 年起如何开始学习 ABP 框架系列文章-开篇有益

2019 年起如何开始学习 ABP 框架系列文章-开篇有益 [[TOC]] 本系列文章推荐阅读地址为:52ABP 开发文档 https://www.52abp.com/Wiki/52abp/latest/Welcome-to-52abp 本文的目的是为了让刚刚接触 ABP 框架的同学或者准备接触 ABP 框架的同学,能够理解和搞明白 ABP 框架到底是怎么回事,毕竟它发展了好几年的时间.社区中有很多人做 了 ABP 的资料和文章包括我自己也建立了 52ABP,社区中还有 ABPplus 等等的内

Eclipse+CXF框架开发Web服务实战

一. 说明 采用CXF框架开发webservice. 所用软件及版本如下. ? 操作系统:Window XP SP3. ? JDK:JDK1.6.0_07,http://www.oracle.com/technetwork/java/javase/downloads/index.html. ? Tomcat:apache-tomcat-6.0.14.exe,http://tomcat.apache.org/. ? IDE:eclipse-jee-juno-SR1-win32.zip,http:/

详解ABP框架的多租户

(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:ABP框架对多租户场景提供了很好的支持,内建了多租户的处理机制,今天我们来深入解析一下这一特性. 最近在基于ABP框架(ASP.NET Boilerplate)开发了一个SaaS.所以接下来可能会时不时分享一下ABP方面的文章.今天来介绍一下ABP对多租户提供的支持特性. ABP简介 ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个

ABP框架详解(五)Navigation

ABP框架中的Navigation功能用于管理业务系统中所有可用的菜单导航控件,通常在业务系统的首页会有一个全局性的导航菜单,JD商城,天猫,猪八戒网莫不如是.所以为方便起见,Navigation功能默认定义了一个"MainMenu"菜单添加到缓存字典中.该Navigation功能与普通ERP项目中可定制动态生成的导航菜单最大的区别应该是每一个菜单定义(MenuItemDefinition)可以设置一个权限只有用户拥有权限才会显示给该用户,控制更加的细更加的松耦合不是直接绑定到某个用户