从壹开始前后端分离 [.netCore 填坑 ] 三十二║ 四种方法快速实现项目的半自动化搭建

缘起

哈喽大家周四好呀,这个国庆过的真是懒洋洋呀,不知道大家的学习动力咋样了,刚一上班本人手中的项目也增加了,但是还是要抽出时间学习哒,为了不让老板大大天天催,所以更新会慢点儿 [ 哭笑 ] :bowtie:,不过在我的推荐下,公司下一个项目要我负责前后端分离,终于可以将这些派上用场了,中间的坑也会在以后的文章中,慢慢补充出来。

这几天简单想了想,还没有想好要开什么系列,就想到QQ群里有小伙伴问的较多的一些问题以及前边系列文章中的提到的,但是没有深入讲到的,再填一下坑,这样才是完整的嘛,大家要是看到之前的有任何不清楚的,或者想扩展的,可以在群里说一下,或者留言,我都会说到的。今天呢,就说说如果半自动化搭建项目,这个坑来自于之前的项目搭建文章《框架之六 || API项目整体搭建 6.1 仓储模式》的伏笔之一 —— 如何使用 T4 模板搭建项目。当然,我们不仅是简单说说实体类的创建,也会横向和纵向的讨论下,一个项目如何通过代码实现半自动化搭建。

说到这里大家应该已经明白了这篇文章的写作意图,就是搭建项目的问题,相信大家已经开发了几年了,都有自己的一套行之有效的办法或者是经验,当然这里就不说付费的了,付费的框架何其多,比如迪西客,普元或者力软等等,这都是好用的功能强大的付费框架,既然说技术,就不说付费的,咱们自己写,这里简单概括下今天要说到的:

0、手动粘贴复制 —— 可能现在还有小伙伴在用这个办法,效率是真的很低,虽然我偶尔也用哈哈,这里就不说了

1、动软代码生成器 —— 这个我入门的时候使用的神器,用了好久

2、通过 VSTO 来编写 Excel ,实现每一层的代码设计 —— 我没用过,但是见过,一个不错的思路

3、T4 模板 —— 这个是我这两年遇到的又一个神器,也是一直使用的,强烈推荐的

4、SqlSuagr(ORM)—— 自封装的一套逻辑,当然其他ORM也有,比如EF就很好

今天咱们这几个方法都会说到,主要还是说一下通过 T4 模板,来把我们的整体框架半自动化起来。最终的效果截图是这样的:

框架截图

image

一、动软代码生成器搭建整体项目框架

相信很多的小伙伴应该使用过这个工具,还是很不错的,我也是用了有一段时间,虽然很小,但是功能确实很丰富,无论是生成实体类,还是生成简单三层,还是工厂模式等等,都可以使用,不仅支持单个表文件的操作,也支持整个数据库的批量生成,这里具体的不做叙述,因为不是本篇文章的重点,这里简单的说下使用方法以及效果图:

1、安装与使用

地址:http://www.maticsoft.com/download.aspx

安装还是很简单的,就是普通的 next ,想用的可以试试,这里就不做叙述了。

安装成功后,我们可以看到,通过连接相应的服务器,找到适当的数据库,就可以操作了,主要还是通过一套 cmt 模板来生成对应的 .cs 类文件,这些模板我们可以使用系统自带的(比如工厂模式模板),也可以自定义编辑,以满足我们不同项目的具体需求,我这里就演示下,如何把我的博客数据库(还是咱们一直用的这个数据库),生成简单工程框架。

image

经过短暂的等待后,我们的全部框架就这么出来了

image

当然这些后缀名等都可以配置,这里就不细说了,大家下载随便看一下数据访问层的内容。

提示:如果你不想下载改软件,又想看看具体的代码,我已经提交到咱们的git 了,在wwwroot 文件夹中,大家可以看看

截图

image

打开看一下,基本的 CURD 方法都有,也有一些存储过程的调用等,而且可以自定义扩展,不得不说是一个神器,如果要开发 ADO.NET 多层,这个方法也不失为一个好的方案。

优点大家都看到了,快速,内容简洁丰富,各种存储过程分页方法很方便,不言而喻;

但是缺点也是有的,最大的就是不能像其他ORM框架那样,实现 Code First ,只能单纯的讲数据库信息提取出来;而且在编辑模板方面也没有 T4 那么容易操作,修改模板代码不是很方便,学习的难度稍微大了一些。

总结:如果不想用 T4 模板手动写模板代码,又不想引入框架,只想用 ADO.NET 搭建多层框架,动软代码生成器是一个不错的选择。

二、通过 VSTO 编写 Excel 来搭建整体项目框架

1、什么是VSTO?

VSTO(Visual Studio Tools For Office)就像名字一样,是Visual Studio开发Office的工具集。只是一套工具,用于简化.NET的Office开发。能够生成com组件或者标准dll的都能开发Office,比如c/c++,VB6,Delphi等等。原生.NET当然也可以。微软觉得.net开发Office不够方便,VSTO便诞生了。

1.决定要用.NET开发Office

2.在所有.NET开发Office的途径(原始,各种工具)中,选择了VSTO

2、如何新建一个 VSTO 项目

网上有一个栗子,大家可以参看着自己玩一玩:https://www.cnblogs.com/crhdyl/p/5289307.html。 对于这个方法,大家自己玩一玩就好了,这里就不说具体的教程了,毕竟不是本文重点。

QQ群里有一个小伙伴大神,他通过 VSTO 来写的一个框架,其实就是通过命令来将固定的格式进行输出,这里盗用一下他的图,应该还好吧,因为不知道他的博客园账号,就先不@他了,个人感觉这个还是很不错的,看着很溜

image

这个方法我现在也在学,不过只是做一个知识扩展来用,好处是,我们自己做一个Excel 工具后,走到哪里都可以使用,还很方便,自己随便自定义,不用受项目或者数据库的环境影响,特别是做展示的时候,很直观,比如开项目研讨会的时候,几个人讨论数据库表结构呀,生成的方法呀,总不能一个个软件都打开吧;

但是也有一些问题,毕竟局限性有些强,比如好像不能直接操作数据库,在大数据结构中,效率不高,而且不能直接生成文件,需要拷贝操作等;

通过上边两个栗子可以看出来,一个是连接数据库快捷,文件生成方便;一个自定义逻辑性强,展示直观,那有没有办法可以将两个优点结合起来呢,没错,就是T4模板了,耐心看完下边的讲解,你会发现很强大。

三、通过 T4 模板搭建整体项目框架

1、什么是 T4 模板

** T4 (Text Template Transformation Toolkit) 是微软官方在 VisualStudio 2008+ 中开始使用的代码生成引擎。在 Visual Studio 中,“T4 文本模板”是由一些文本块和控制逻辑组成的混合模板,它可以生成文本文件。 在 Visual C# 或 Visual Basic 中,控制逻辑编写为程序代码的片段。生成的文件可以是任何类型的文本,例如网页、资源文件或任何语言的程序源代码。现在的VS中只要与代码生成相关的场景基本上都能找T4的身影,比如MVC的视图模板Entity Framwork的DataContext**模板等等。

2、在项目中通过T4实现数据库生成实体类

这里就不具体讲解 T4 语法了,大家可以自行学习,其实很简单,主要还是 C# 代码,下边你看过之后就能懂了,咱们首先先实现之前留下的一个伏笔 —— 将我们的数据库表利用T4 模板生成实体类,也就是 DbFirst。

**1、首先在我们的项目中,新建一个类库 Blog.Core.FrameWork **

2、在该类库下,新建文件夹 Blog.Core.FrameWork.Entity,用于单独存放我们的模板以及生成的实体类文件

image

3、在类库的根目录新建 ModelAuto.ttinclude 模板文件,用来控制我们的文件生成配置

可以直接新建一个文本文档,然后重命名即可

提示:初始状态下,代码不是高亮的,大家可以安装插件:下载地址,不过个人感觉并不是很好用。

//引入命名空间
<#@ assembly name="System.Core"#>
<#@ assembly name="EnvDTE"#>
<#@ import namespace="System.Collections.Generic"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>

<#+
//定义管理者 manager 实体类
class Manager
{ //定义一个 block 块,主要是应用在批量生产中
    public struct Block { public String Name; public int Start, Length;
    } public List<Block> blocks = new List<Block>(); public Block currentBlock; public Block footerBlock = new Block(); public Block headerBlock = new Block(); public ITextTemplatingEngineHost host; public ManagementStrategy strategy; public StringBuilder template; public String OutputPath { get; set; } //构造函数,包含 host主机,模板,输出路径,创建管理策略
    public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) { this.host = host; this.template = template;
        OutputPath = String.Empty;
        strategy = ManagementStrategy.Create(host);
    } //开辟一个 block 块
    public void StartBlock(String name) {
        currentBlock = new Block { Name = name, Start = template.Length };
    } public void StartFooter() {
        footerBlock.Start = template.Length;
    } public void EndFooter() {
        footerBlock.Length = template.Length - footerBlock.Start;
    } public void StartHeader() {
        headerBlock.Start = template.Length;
    } public void EndHeader() {
        headerBlock.Length = template.Length - headerBlock.Start;
    } public void EndBlock() {
        currentBlock.Length = template.Length - currentBlock.Start;
        blocks.Add(currentBlock);
    } //定义进程,用来将所有的 blocks 块执行出来
    public void Process(bool split) {
        String header = template.ToString(headerBlock.Start, headerBlock.Length);
        String footer = template.ToString(footerBlock.Start, footerBlock.Length);
        blocks.Reverse(); foreach(Block block in blocks) {//遍历 //输出文件
            String fileName = Path.Combine(OutputPath, block.Name); if (split) {
                String content = header + template.ToString(block.Start, block.Length) + footer;
                strategy.CreateFile(fileName, content);
                template.Remove(block.Start, block.Length);
            } else {
                strategy.DeleteFile(fileName);
            }
        }
    }
} //定义管理策略类
class ManagementStrategy
{ internal static ManagementStrategy Create(ITextTemplatingEngineHost host) { return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
    } internal ManagementStrategy(ITextTemplatingEngineHost host) { } internal virtual void CreateFile(String fileName, String content) {
        File.WriteAllText(fileName, content);
    } internal virtual void DeleteFile(String fileName) { if (File.Exists(fileName))
            File.Delete(fileName);
    }
} class VSManagementStrategy : ManagementStrategy
{ private EnvDTE.ProjectItem templateProjectItem; internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) {
        IServiceProvider hostServiceProvider = (IServiceProvider)host; if (hostServiceProvider == null) throw new ArgumentNullException("Could not obtain hostServiceProvider");

        EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) throw new ArgumentNullException("Could not obtain DTE from host");

        templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
    } //创建文件
    internal override void CreateFile(String fileName, String content) { base.CreateFile(fileName, content);
        ((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
    } //删除文件
    internal override void DeleteFile(String fileName) {
        ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
    } //根据文件名删除文件
    private void FindAndDeleteFile(String fileName) { foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { if (projectItem.get_FileNames(0) == fileName) {
                projectItem.Delete(); return;
            }
        }
    }
}#>

4、还是在类库根目录下新建 DbHelper.ttinclude 模板,主要是数据库操作类

代码太多就不放了

具体的代码就不细讲了,直接拷贝,或者下载我写好的代码即可。再次说明下,本文不会对 T4 语法继续深入研究。

image

5、实现生成全部实体类的模型设计,在Blog.Core.FrameWork.Entity文件夹下,新建 Blog.Core.FrameWork.Entity.tt 模板

image

填写代码

//如果要获取主机信息,记得把 hostspecific 设置成true
<#@ template debug="false" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>

//导入命名空间组件
<#@ assembly name="System.Data" #>
<#@ assembly name="System.xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.DataSetExtensions.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

//引入我们的公共模板文件
<#@ include file="$(ProjectDir)DbHelper.ttinclude"  #>
<#@ include file="$(ProjectDir)ModelAuto.ttinclude"    #>

//定义我们的输出文件夹
<# var OutputPath1 = Path.GetDirectoryName(Host.TemplateFile)+"\\work"; if (!Directory.Exists(OutputPath1))
    {
        Directory.CreateDirectory(OutputPath1);
    } var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = OutputPath1 }; 
#>

//-------------------------------------------------------------------- // 此代码由T4模板自动生成 // 老张的哲学 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> // 注意更新后会改变相应代码。 //--------------------------------------------------------------------
<# var tableName=config.TableName;//获取config配置中的表名,为单一生产使用
 #>
<# if(tableName!=""){//如果表名有值,表示是生成单一文件
    #>  
//引用命名空间
using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
    /// <#=tableName#>
    /// </summary>    
    public class <#=tableName#>//可以在这里加上基类等
 { //将全部字段遍历出来
<# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, config.TableName)){#>
      public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } <#}#> }
} //如果为空,表示要将整个数据库都生成出来
<# 
    } else{ 

    #>
    //连接数据库,打开 connect 连接
<# 
    SqlConnection conn = new SqlConnection(config.ConnectionString); 
    conn.Open(); 
    System.Data.DataTable schema = conn.GetSchema("TABLES"); 
 #>
 //遍历全部数据库表
<# foreach(System.Data.DataRow row in schema.Rows) 
    {  #>   

    //开始启动block块,参数是实体类文件名
    <# 
        manager.StartBlock(row["TABLE_NAME"]+".cs");
     #>
    //----------<#=row["TABLE_NAME"].ToString()#>开始----------

using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
    /// <#=tableName#>
    /// </summary>    
    public class <#=row["TABLE_NAME"].ToString()#>//可以在这里加上基类等
 { //将该表下的字段都遍历出来,可以自定义获取数据描述等信息
<# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, row["TABLE_NAME"].ToString() )){ #>

      public <#= column.CSharpType#> <# if(column.CommonType.IsValueType && column.IsNullable){#> ?<#}#> <#=column.ColumnName #> { get; set; } <#}#> }
} //----------<#=row["TABLE_NAME"].ToString()#>结束----------
    <# 
        manager.EndBlock(); 
        } 
        manager.Process(true);
    }
    #>

这里我写的比较凌乱,大概就是这个逻辑,其实和 C# 特别相像,主要简单的看一看,就都会明白,这个时候我们保存文件 ctrl+S,如果你连接数据库正确的话,会发现已经生成了全部实体类文件。

image

随便打开一个实体类,就可以发现,和我们自己写的是一样的:是不是很方便!

//----------BlogArticle开始----------
using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
    /// BlogArticle /// </summary>    
    public class BlogArticle//可以在这里加上基类等
 { //将该表下的字段都遍历出来,可以自定义获取数据描述等信息
      public int  bID { get; set; } public string  bsubmitter { get; set; } public string  btitle { get; set; } public string  bcategory { get; set; } public string  bcontent { get; set; } public int  btraffic { get; set; } public int  bcommentNum { get; set; } public DateTime  bUpdateTime { get; set; } public DateTime  bCreateTime { get; set; } public string  bRemark { get; set; }

    }
} //----------BlogArticle结束----------

这个时候你会问,这个既然这么好,能全部生成其他层么?没错,答案当然是肯定的!

3、实现整个系统框架模板

因为篇幅的问题,这里就不把代码粘贴出来了,大家自己去Github 上获取即可,最终的效果是酱紫的:

image

是不是很神器!整个系统框架就在一瞬间就加载出来了,大家下载好后,只需要 ctrl + S 保存一下 tt 模板文件,就能全部生成,只需要把精力放到 Base 基类/基接口即可,以后数据库表结构无论如何变化都不怕!

四、SqlSugar等ORM框架来实现 DbFirst

总结来说,其实一般的 ORM 框架,都是集成的 T4模板,EF是如此,咱们的SqlSugar 也是这样,已经封装好了相应的逻辑代码:

using (SqlSugarClient db =SqlSugarInstance.GetInstance())
            { try { // db.CodeFirst.InitTables(typeof(实体类model));
                     db.DbFirst.Where("数据库表名").CreateClassFile("D:\\Demo\\2.cs");//生成文件的·地址 
 } catch (Exception ex)
                {
                }

            }

还记得咱们 Repisitory 层中的 DbContext.cs 文件么,里边我已经封装好了,直接在 BaseRepository.cs 或者自己写的任何文件中调用就行,只不过这里仅仅是 Model 层的。这里就不多说了。

image

五、结语

今天也是在忙碌中,抽出时间写了写,主要是给大家分享了下,如何快速的半自动化的实现项目搭建,也算是一个小技巧了,重点说明了下 T4模板搭建全部层文件,个人感觉还是可行的,主要是灵活性很强,需要的小伙伴可以好好的研究一下,继续奋斗中!!!

六、Github代码

https://github.com/anjoy8/Blog.Core

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容