在日常开发中,有时会遇到一些相似的代码,甚至是只要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 ?? ,
templatePageJson.Date,
templatePageJson.Index - ,
templatePageJson.Index + ,
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 @Model.ProjectNameSpace.@Model.ProjectModule.@(Model.EntityName)s.Dto; namespace @Model.ProjectNameSpace.@Model.ProjectModule.@(Model.EntityName)s
{
/// <summary>
/// @(Model.EntityDescription)应用服务接口
/// </summary>
public interface I@(Model.EntityName)AppService : IApplicationService
{
/// <summary>
/// 获取@(Model.EntityDescription)数据集合
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<PagedResultDto<@(Model.EntityName)ListDto>> GetPaged@(Model.EntityName)(GetPaged@(Model.EntityName)Input input); /// <summary>
/// 获取@(Model.EntityDescription)编辑信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<Get@(Model.EntityName)ForEditOutput> Get@(Model.EntityName)ForEdit(NullableIdDto<@Model.EntityKeyType> input); /// <summary>
/// 创建或修改@(Model.EntityDescription)信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CreateOrUpdate@(Model.EntityName)(CreateOrUpdate@(Model.EntityName)Input input); /// <summary>
/// 删除@(Model.EntityDescription)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Delete@(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()].ToString(),
Name = dataReader[dataReader.GetName()].ToString(),
OrdinalPosition = StringExtension.GetValueOrNull<int>(dataReader[dataReader.GetName()].ToString()),
IsNullable = dataReader[dataReader.GetName()].ToString(),
DataType = dataReader[dataReader.GetName()].ToString(),
CharacterMaximumLength = StringExtension.GetValueOrNull<int>(dataReader[dataReader.GetName()].ToString()),
ColumnKey = dataReader[dataReader.GetName()].ToString(),
ColumnComment = dataReader[dataReader.GetName()].ToString(),
};
sqlDatasList.Add(columnInfo);
} dataReader.Close();
SqlConnection.Close();
return sqlDatasList;

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

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

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

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

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

  1. X-Admin&ABP框架开发-消息通知

    业务型网站使用过程中,消息通知是一个不可或缺的功能,采用站内通知.短信通知.邮件通知.微信通知等等各种方式都有,ABP框架对这部分工作已经封装的很好了,站在巨人的肩膀上,一览全貌,带来的就是心情舒畅. ...

  2. X-Admin&ABP框架开发-系统日志

    网站正常运行中有时出现异常在所难免,查看系统运行日志分析问题并能够根据错误信息快速解决问题尤为重要,ABP对于系统运行日志这块已经做了很好的处理,默认采用的Log4Net已经足够满足开发过程中的需要了 ...

  3. X-Admin&ABP框架开发-版本管理

    多租户系统中,针对于不同租户开放不同功能,或是按照不同功能进行收费管理,需要从宿主本身去管理租户的版本信息,如同酒店人员对不同房间收取不同费用,依据房间内部设施,房间大小等设置不同收费标准.Abp系统 ...

  4. X-Admin&ABP框架开发-设置管理

    在网站开发中,设置是不可缺少的一环,如用户设置.系统设置.甚至是租户设置等.ABP对于设置的管理已经做了很好的处理,我们可以借助巨人的力量来完成我们的冒险. ABP官网地址:https://aspne ...

  5. X-Admin&ABP框架开发-RBAC

    在业务系统需求规划过程中,通常对于诸如组织机构.用户和角色等这种基础功能,通常是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来讲,是系统的基石,整个业务体系是建立于这部分基石之上的,当然, ...

  6. X-Admin&ABP框架开发-数据字典

    在业务型的系统开发中,我们需要维护各种个样的类型,比如客户类型.客户行业.商品类型等等,这些类型往往信息量不多,并且相似度极高,如果采用一类型一表去设计,将会造成极大的工作量,通过将这部分类型的信息进 ...

  7. X-Admin&ABP框架开发-租户管理

    软件即服务概念的推动,定制化到通用化的发展,用一套代码完成适应不同企业的需求,利用多租户技术可以去做到这一点.ABP里提供了多租户这一概念并且也在Zero模块中实现了这一概念. 一.多租户的概念 单部 ...

  8. 高薪诚聘熟悉ABP框架的.NET高级开发工程师(2016年7月28日重发)

    招聘单位是ABP架构设计交流群(134710707)群主阳铭所在的公司-上海运图贸易有限公司 招聘岗位:.NET高级开发工程师工作地点:上海-普陀区 [公司情况]上海运图贸易有限公司,是由易迅网的创始 ...

  9. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

随机推荐

  1. LeetCode110 Balanced Binary Tree

    Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...

  2. 基于 springMVC 的 RESTful HTTP API 实践(服务端)

    理解 REST REST(Representational State Transfer),中文翻译叫"表述性状态转移".是 Roy Thomas Fielding 在他2000年 ...

  3. Error While Loading Shared Libraries, Cannot Open Shared Object File

    In the "I wish the Internet had an actual correct answer" category comes a question from a ...

  4. uva 11665 Chinese Ink (几何+并查集)

    UVA 11665 随便给12的找了一道我没做过的几何基础题.这题挺简单的,不过uva上通过率挺低,通过人数也不多. 题意是要求给出的若干多边形组成多少个联通块.做的时候要注意这题是不能用double ...

  5. IntelliJ IDEA和Eclipse设置JVM运行参数

    打开 IDEA 安装目录,看到有一个 bin 目录,其中有两个 vmoptions 文件,需针对不同的JDK进行配置: 32 位:idea.exe.vmoptions64 位:idea64.exe.v ...

  6. 第三期 行为规划——10.用C++实现变道函数

    在之前的测验中,我们设计了一个成本函数,高速公路上到达一个目标选择一条车道. 公式中,Δd是车道间的纵向距离,Δs是车辆到目标之间的距离. 在这个测验中,需要用c++实现代价函数,但是这里有一个变换, ...

  7. 一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    Adam那么棒,为什么还对SGD念念不忘 (1) —— 一个框架看懂优化算法 机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着 ...

  8. Pytorch Bi-LSTM + CRF 代码详解

    久闻LSTM + CRF的效果强大,最近在看Pytorch官网文档的时候,看到了这段代码,前前后后查了很多资料,终于把代码弄懂了.我希望在后来人看这段代码的时候,直接就看我的博客就能完全弄懂这段代码. ...

  9. 不通过DataRow,直接往DataTable中添加新行DataTable.LoadDataRow(object[],bool)

    DataTable dtver = new DataTable();                dtver.Columns.Add("VERSION");            ...

  10. [转]React入门看这篇就够了

    摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...