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


    更新

    1、更新小伙伴 @大龄Giser 提出好点子:试试VS的插件扩展:VSIX、ItemProject等,将T4模板给制作插件,这里先记下,有懂的小伙伴可以自己先试试,我会在以后更新。

    2、感谢小伙伴  @博客园董事长 的测试和指正,本文 T4 模板已经支持 Oracle

    3、再次感谢小伙伴 @大龄Giser  关于 使用VSIX+ProjectTemplate创建项目模板,提供开源支持:https://www.cnblogs.com/OlderGiser/p/9796403.html

    4、感谢群小伙伴 @jamee1696  提供技术支持,本文 T4 模板已经支持 Mysql

    5、如果感觉 Blog.Core.Framework 层比较笨重,我单写了一个简单的生成框架文件的接口,可以查看 DbFirstController.cs 【推荐使用】

    缘起

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

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

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

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

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

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

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

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

    5、VSIX 扩展插件 —— 新的处理方案

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

    框架截图

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

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

    1、安装与使用

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

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

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

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

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

    /**  版本信息模板在安装目录下,可自行修改。
    * BlogArticle.cs
    *
    * 功 能: N/A
    * 类 名: BlogArticle
    *
    * Ver    变更日期             负责人  变更内容
    * ───────────────────────────────────
    * V0.01  2018/10/8 23:46:56   N/A    初版
    *
    * Copyright (c) 2012 Maticsoft Corporation. All rights reserved.
    *┌──────────────────────────────────┐
    *│ 此技术信息为本公司机密信息,未经本公司书面同意禁止向第三方披露. │
    *│ 版权所有:动软卓越(北京)科技有限公司              │
    *└──────────────────────────────────┘
    */
    using System;
    using System.Data;
    using System.Text;
    using System.Data.SqlClient;
    using Maticsoft.IDAL;
    using Maticsoft.DBUtility;//Please add references
    namespace Maticsoft.SQLServerDAL
    {
        /// <summary>
        /// 数据访问类:BlogArticle
        /// </summary>
        public partial class BlogArticle:IBlogArticle
        {
            public BlogArticle()
            {}
            #region  BasicMethod
    
            /// <summary>
            /// 得到最大ID
            /// </summary>
            public int GetMaxId()
            {
            return DbHelperSQL.GetMaxID("bID", "BlogArticle"); 
            }
    
            /// <summary>
            /// 是否存在该记录
            /// </summary>
            public bool Exists(int bID)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("select count(1) from BlogArticle");
                strSql.Append(" where bID=@bID");
                SqlParameter[] parameters = {
                        new SqlParameter("@bID", SqlDbType.Int,4)
                };
                parameters[0].Value = bID;
    
                return DbHelperSQL.Exists(strSql.ToString(),parameters);
            }
    
    
            /// <summary>
            /// 增加一条数据
            /// </summary>
            public int Add(Maticsoft.Model.BlogArticle model)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("insert into BlogArticle(");
                strSql.Append("bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark)");
                strSql.Append(" values (");
                strSql.Append("@bsubmitter,@btitle,@bcategory,@bcontent,@btraffic,@bcommentNum,@bUpdateTime,@bCreateTime,@bRemark)");
                strSql.Append(";select @@IDENTITY");
                SqlParameter[] parameters = {
                        new SqlParameter("@bsubmitter", SqlDbType.NVarChar,60),
                        new SqlParameter("@btitle", SqlDbType.NVarChar,256),
                        new SqlParameter("@bcategory", SqlDbType.NVarChar,-1),
                        new SqlParameter("@bcontent", SqlDbType.Text),
                        new SqlParameter("@btraffic", SqlDbType.Int,4),
                        new SqlParameter("@bcommentNum", SqlDbType.Int,4),
                        new SqlParameter("@bUpdateTime", SqlDbType.DateTime),
                        new SqlParameter("@bCreateTime", SqlDbType.DateTime),
                        new SqlParameter("@bRemark", SqlDbType.NVarChar,-1)};
                parameters[0].Value = model.bsubmitter;
                parameters[1].Value = model.btitle;
                parameters[2].Value = model.bcategory;
                parameters[3].Value = model.bcontent;
                parameters[4].Value = model.btraffic;
                parameters[5].Value = model.bcommentNum;
                parameters[6].Value = model.bUpdateTime;
                parameters[7].Value = model.bCreateTime;
                parameters[8].Value = model.bRemark;
    
                object obj = DbHelperSQL.GetSingle(strSql.ToString(),parameters);
                if (obj == null)
                {
                    return 0;
                }
                else
                {
                    return Convert.ToInt32(obj);
                }
            }
            /// <summary>
            /// 更新一条数据
            /// </summary>
            public bool Update(Maticsoft.Model.BlogArticle model)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("update BlogArticle set ");
                strSql.Append("bsubmitter=@bsubmitter,");
                strSql.Append("btitle=@btitle,");
                strSql.Append("bcategory=@bcategory,");
                strSql.Append("bcontent=@bcontent,");
                strSql.Append("btraffic=@btraffic,");
                strSql.Append("bcommentNum=@bcommentNum,");
                strSql.Append("bUpdateTime=@bUpdateTime,");
                strSql.Append("bCreateTime=@bCreateTime,");
                strSql.Append("bRemark=@bRemark");
                strSql.Append(" where bID=@bID");
                SqlParameter[] parameters = {
                        new SqlParameter("@bsubmitter", SqlDbType.NVarChar,60),
                        new SqlParameter("@btitle", SqlDbType.NVarChar,256),
                        new SqlParameter("@bcategory", SqlDbType.NVarChar,-1),
                        new SqlParameter("@bcontent", SqlDbType.Text),
                        new SqlParameter("@btraffic", SqlDbType.Int,4),
                        new SqlParameter("@bcommentNum", SqlDbType.Int,4),
                        new SqlParameter("@bUpdateTime", SqlDbType.DateTime),
                        new SqlParameter("@bCreateTime", SqlDbType.DateTime),
                        new SqlParameter("@bRemark", SqlDbType.NVarChar,-1),
                        new SqlParameter("@bID", SqlDbType.Int,4)};
                parameters[0].Value = model.bsubmitter;
                parameters[1].Value = model.btitle;
                parameters[2].Value = model.bcategory;
                parameters[3].Value = model.bcontent;
                parameters[4].Value = model.btraffic;
                parameters[5].Value = model.bcommentNum;
                parameters[6].Value = model.bUpdateTime;
                parameters[7].Value = model.bCreateTime;
                parameters[8].Value = model.bRemark;
                parameters[9].Value = model.bID;
    
                int rows=DbHelperSQL.ExecuteSql(strSql.ToString(),parameters);
                if (rows > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
    
            /// <summary>
            /// 删除一条数据
            /// </summary>
            public bool Delete(int bID)
            {
                
                StringBuilder strSql=new StringBuilder();
                strSql.Append("delete from BlogArticle ");
                strSql.Append(" where bID=@bID");
                SqlParameter[] parameters = {
                        new SqlParameter("@bID", SqlDbType.Int,4)
                };
                parameters[0].Value = bID;
    
                int rows=DbHelperSQL.ExecuteSql(strSql.ToString(),parameters);
                if (rows > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            /// <summary>
            /// 批量删除数据
            /// </summary>
            public bool DeleteList(string bIDlist )
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("delete from BlogArticle ");
                strSql.Append(" where bID in ("+bIDlist + ")  ");
                int rows=DbHelperSQL.ExecuteSql(strSql.ToString());
                if (rows > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
    
    
            /// <summary>
            /// 得到一个对象实体
            /// </summary>
            public Maticsoft.Model.BlogArticle GetModel(int bID)
            {
                
                StringBuilder strSql=new StringBuilder();
                strSql.Append("select  top 1 bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark from BlogArticle ");
                strSql.Append(" where bID=@bID");
                SqlParameter[] parameters = {
                        new SqlParameter("@bID", SqlDbType.Int,4)
                };
                parameters[0].Value = bID;
    
                Maticsoft.Model.BlogArticle model=new Maticsoft.Model.BlogArticle();
                DataSet ds=DbHelperSQL.Query(strSql.ToString(),parameters);
                if(ds.Tables[0].Rows.Count>0)
                {
                    return DataRowToModel(ds.Tables[0].Rows[0]);
                }
                else
                {
                    return null;
                }
            }
    
    
            /// <summary>
            /// 得到一个对象实体
            /// </summary>
            public Maticsoft.Model.BlogArticle DataRowToModel(DataRow row)
            {
                Maticsoft.Model.BlogArticle model=new Maticsoft.Model.BlogArticle();
                if (row != null)
                {
                    if(row["bID"]!=null && row["bID"].ToString()!="")
                    {
                        model.bID=int.Parse(row["bID"].ToString());
                    }
                    if(row["bsubmitter"]!=null)
                    {
                        model.bsubmitter=row["bsubmitter"].ToString();
                    }
                    if(row["btitle"]!=null)
                    {
                        model.btitle=row["btitle"].ToString();
                    }
                    if(row["bcategory"]!=null)
                    {
                        model.bcategory=row["bcategory"].ToString();
                    }
                    if(row["bcontent"]!=null)
                    {
                        model.bcontent=row["bcontent"].ToString();
                    }
                    if(row["btraffic"]!=null && row["btraffic"].ToString()!="")
                    {
                        model.btraffic=int.Parse(row["btraffic"].ToString());
                    }
                    if(row["bcommentNum"]!=null && row["bcommentNum"].ToString()!="")
                    {
                        model.bcommentNum=int.Parse(row["bcommentNum"].ToString());
                    }
                    if(row["bUpdateTime"]!=null && row["bUpdateTime"].ToString()!="")
                    {
                        model.bUpdateTime=DateTime.Parse(row["bUpdateTime"].ToString());
                    }
                    if(row["bCreateTime"]!=null && row["bCreateTime"].ToString()!="")
                    {
                        model.bCreateTime=DateTime.Parse(row["bCreateTime"].ToString());
                    }
                    if(row["bRemark"]!=null)
                    {
                        model.bRemark=row["bRemark"].ToString();
                    }
                }
                return model;
            }
    
            /// <summary>
            /// 获得数据列表
            /// </summary>
            public DataSet GetList(string strWhere)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("select bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark ");
                strSql.Append(" FROM BlogArticle ");
                if(strWhere.Trim()!="")
                {
                    strSql.Append(" where "+strWhere);
                }
                return DbHelperSQL.Query(strSql.ToString());
            }
    
            /// <summary>
            /// 获得前几行数据
            /// </summary>
            public DataSet GetList(int Top,string strWhere,string filedOrder)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("select ");
                if(Top>0)
                {
                    strSql.Append(" top "+Top.ToString());
                }
                strSql.Append(" bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark ");
                strSql.Append(" FROM BlogArticle ");
                if(strWhere.Trim()!="")
                {
                    strSql.Append(" where "+strWhere);
                }
                strSql.Append(" order by " + filedOrder);
                return DbHelperSQL.Query(strSql.ToString());
            }
    
            /// <summary>
            /// 获取记录总数
            /// </summary>
            public int GetRecordCount(string strWhere)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("select count(1) FROM BlogArticle ");
                if(strWhere.Trim()!="")
                {
                    strSql.Append(" where "+strWhere);
                }
                object obj = DbHelperSQL.GetSingle(strSql.ToString());
                if (obj == null)
                {
                    return 0;
                }
                else
                {
                    return Convert.ToInt32(obj);
                }
            }
            /// <summary>
            /// 分页获取数据列表
            /// </summary>
            public DataSet GetListByPage(string strWhere, string orderby, int startIndex, int endIndex)
            {
                StringBuilder strSql=new StringBuilder();
                strSql.Append("SELECT * FROM ( ");
                strSql.Append(" SELECT ROW_NUMBER() OVER (");
                if (!string.IsNullOrEmpty(orderby.Trim()))
                {
                    strSql.Append("order by T." + orderby );
                }
                else
                {
                    strSql.Append("order by T.bID desc");
                }
                strSql.Append(")AS Row, T.*  from BlogArticle T ");
                if (!string.IsNullOrEmpty(strWhere.Trim()))
                {
                    strSql.Append(" WHERE " + strWhere);
                }
                strSql.Append(" ) TT");
                strSql.AppendFormat(" WHERE TT.Row between {0} and {1}", startIndex, endIndex);
                return DbHelperSQL.Query(strSql.ToString());
            }
    
            /*
            /// <summary>
            /// 分页获取数据列表
            /// </summary>
            public DataSet GetList(int PageSize,int PageIndex,string strWhere)
            {
                SqlParameter[] parameters = {
                        new SqlParameter("@tblName", SqlDbType.VarChar, 255),
                        new SqlParameter("@fldName", SqlDbType.VarChar, 255),
                        new SqlParameter("@PageSize", SqlDbType.Int),
                        new SqlParameter("@PageIndex", SqlDbType.Int),
                        new SqlParameter("@IsReCount", SqlDbType.Bit),
                        new SqlParameter("@OrderType", SqlDbType.Bit),
                        new SqlParameter("@strWhere", SqlDbType.VarChar,1000),
                        };
                parameters[0].Value = "BlogArticle";
                parameters[1].Value = "bID";
                parameters[2].Value = PageSize;
                parameters[3].Value = PageIndex;
                parameters[4].Value = 0;
                parameters[5].Value = 0;
                parameters[6].Value = strWhere;    
                return DbHelperSQL.RunProcedure("UP_GetRecordByPage",parameters,"ds");
            }*/
    
            #endregion  BasicMethod
            #region  ExtensionMethod
    
            #endregion  ExtensionMethod
        }
    }
    View Code

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

    截图

     

    打开看一下,基本的 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 来写的一个框架,其实就是通过命令来将固定的格式进行输出,这里盗用一下他的图,应该还好吧,因为不知道他的博客园账号,就先不@他了,个人感觉这个还是很不错的,看着很溜

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

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

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

    3、下载该工具

    因为有很多小伙伴问到了这个工具,再经得作者同意以后(作者地址https://github.com/DevilX5,给大神点 Star !),拿到了工具和Code,有需要的小伙伴可以玩一玩:

    工具https://files.cnblogs.com/files/laozhang-is-phi/DxAddIn.rar

    Githubhttps://github.com/DevilX5/DxAddIn

    工具使用很简单,下载解压后,就是执行下这个文件,就会在你的Excel中,添加这个服务

    你会在你的Excel中发现这个服务:

    具体的操作,可以按照上边作者的动图来操作。

    三、通过 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,用于单独存放我们的模板以及生成的实体类文件

    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 模板,主要是数据库操作类

    <#+
    class DbHelper
        {
            #region 获取数据库表结构
    
            public static List<DbTable> GetDbTables(string connectionString, string database, string tables = null)
            {
    
                if (!string.IsNullOrEmpty(tables))
                {
                    tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','"));
                }
                #region 执行sql命令
                string sql = string.Format(@"SELECT
                                        obj.name tablename,
                                        schem.name schemname,
                                        idx.rows,
                                        CAST
                                        (
                                            CASE 
                                                WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1
                                                ELSE 0
                                            END 
                                        AS BIT) HasPrimaryKey                                         
                                        from {0}.sys.objects obj 
                                        inner join {0}.dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1
                                        INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id
                                        where type='U' {1}
                                        order by obj.name", database, tables);
                #endregion
                DataTable dt = GetDataTable(connectionString, sql);
                return dt.Rows.Cast<DataRow>().Select(row => new DbTable
                {
                    TableName = row.Field<string>("tablename"),
                    SchemaName = row.Field<string>("schemname"),
                    Rows = row.Field<int>("rows"),
                    HasPrimaryKey = row.Field<bool>("HasPrimaryKey")
                }).ToList();
            }
            #endregion
    
            #region 获取表字段信息
    
            public static List<DbColumn> GetDbColumns(string connectionString, string database, string tableName, string schema = "dbo")
            {
                #region 执行sql命令
                string sql = string.Format(@"
                                        WITH indexCTE AS
                                        (
                                            SELECT 
                                            ic.column_id,
                                            ic.index_column_id,
                                            ic.object_id    
                                            FROM {0}.sys.indexes idx
                                            INNER JOIN {0}.sys.index_columns ic ON idx.index_id = ic.index_id AND idx.object_id = ic.object_id
                                            WHERE  idx.object_id =OBJECT_ID(@tableName) AND idx.is_primary_key=1
                                        )
                                        select
                                        colm.column_id ColumnID,
                                        CAST(CASE WHEN indexCTE.column_id IS NULL THEN 0 ELSE 1 END AS BIT) IsPrimaryKey,
                                        colm.name ColumnName,
                                        systype.name ColumnType,
                                        colm.is_identity IsIdentity,
                                        colm.is_nullable IsNullable,
                                        cast(colm.max_length as int) ByteLength,
                                        (
                                            case 
                                                when systype.name='nvarchar' and colm.max_length>0 then colm.max_length/2 
                                                when systype.name='nchar' and colm.max_length>0 then colm.max_length/2
                                                when systype.name='ntext' and colm.max_length>0 then colm.max_length/2 
                                                else colm.max_length
                                            end
                                        ) CharLength,
                                        cast(colm.precision as int) Precision,
                                        cast(colm.scale as int) Scale,
                                        prop.value Remark
                                        from {0}.sys.columns colm
                                        inner join {0}.sys.types systype on colm.system_type_id=systype.system_type_id and colm.user_type_id=systype.user_type_id
                                        left join {0}.sys.extended_properties prop on colm.object_id=prop.major_id and colm.column_id=prop.minor_id
                                        LEFT JOIN indexCTE ON colm.column_id=indexCTE.column_id AND colm.object_id=indexCTE.object_id                                        
                                        where colm.object_id=OBJECT_ID(@tableName)
                                        order by colm.column_id", database);
                #endregion
                SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100) { Value = string.Format("{0}.{1}.{2}", database, schema, tableName) };
                DataTable dt = GetDataTable(connectionString, sql, param);
                return dt.Rows.Cast<DataRow>().Select(row => new DbColumn()
                {
                    ColumnID = row.Field<int>("ColumnID"),
                    IsPrimaryKey = row.Field<bool>("IsPrimaryKey"),
                    ColumnName = row.Field<string>("ColumnName"),
                    ColumnType = row.Field<string>("ColumnType"),
                    IsIdentity = row.Field<bool>("IsIdentity"),
                    IsNullable = row.Field<bool>("IsNullable"),
                    ByteLength = row.Field<int>("ByteLength"),
                    CharLength = row.Field<int>("CharLength"),
                    Scale = row.Field<int>("Scale"),
                    Remark = row["Remark"].ToString()
                }).ToList();
            }
    
            #endregion
    
            #region 获取 DataTable
    
            public static DataTable GetDataTable(string connectionString, string commandText, params SqlParameter[] parms)
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    SqlCommand command = connection.CreateCommand();
                    command.CommandText = commandText;
                    command.Parameters.AddRange(parms);
                    SqlDataAdapter adapter = new SqlDataAdapter(command);
    
                    DataTable dt = new DataTable();
                    adapter.Fill(dt);
    
                    return dt;
                }
            }
    
            #endregion
    
            #region 获取主键
            public static string GetPrimaryKey(List<DbColumn> dbColumns)
            {
                string primaryKey = string.Empty;
                if (dbColumns!=null&&dbColumns.Count>0)
                {
                    foreach (var item in dbColumns)
                    {
                        if (item.IsPrimaryKey==true)
                        {
                            primaryKey = item.ColumnName;
                        }
                    }
                }
                return primaryKey;
            }
            #endregion
        }
    
    class DbTable
        {
            /// <summary>
            /// 表名称
            /// </summary>
            public string TableName { get; set; }
            /// <summary>
            /// 表的架构
            /// </summary>
            public string SchemaName { get; set; }
            /// <summary>
            /// 表的记录数
            /// </summary>
            public int Rows { get; set; }
    
            /// <summary>
            /// 是否含有主键
            /// </summary>
            public bool HasPrimaryKey { get; set; }
        }
    
    class DbColumn
        {
            /// <summary>
            /// 字段ID
            /// </summary>
            public int ColumnID { get; set; }
    
            /// <summary>
            /// 是否主键
            /// </summary>
            public bool IsPrimaryKey { get; set; }
    
            /// <summary>
            /// 字段名称
            /// </summary>
            public string ColumnName { get; set; }
    
            /// <summary>
            /// 字段类型
            /// </summary>
            public string ColumnType { get; set; }
    
            /// <summary>
            /// 数据库类型对应的C#类型
            /// </summary>
            public string CSharpType
            {
                get
                {
                    return SqlServerDbTypeMap.MapCsharpType(ColumnType);
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            public Type CommonType
            {
                get
                {
                    return SqlServerDbTypeMap.MapCommonType(ColumnType);
                }
            }
    
            /// <summary>
            /// 字节长度
            /// </summary>
            public int ByteLength { get; set; }
    
            /// <summary>
            /// 字符长度
            /// </summary>
            public int CharLength { get; set; }
    
            /// <summary>
            /// 小数位
            /// </summary>
            public int Scale { get; set; }
    
            /// <summary>
            /// 是否自增列
            /// </summary>
            public bool IsIdentity { get; set; }
    
            /// <summary>
            /// 是否允许空
            /// </summary>
            public bool IsNullable { get; set; }
    
            /// <summary>
            /// 描述
            /// </summary>
            public string Remark { get; set; }
        }
    
    class SqlServerDbTypeMap
        {
            public static string MapCsharpType(string dbtype)
            {
                if (string.IsNullOrEmpty(dbtype)) return dbtype;
                dbtype = dbtype.ToLower();
                string csharpType = "object";
                switch (dbtype)
                {
                    case "bigint": csharpType = "long"; break;
                    case "binary": csharpType = "byte[]"; break;
                    case "bit": csharpType = "bool"; break;
                    case "char": csharpType = "string"; break;
                    case "date": csharpType = "DateTime"; break;
                    case "datetime": csharpType = "DateTime"; break;
                    case "datetime2": csharpType = "DateTime"; break;
                    case "datetimeoffset": csharpType = "DateTimeOffset"; break;
                    case "decimal": csharpType = "decimal"; break;
                    case "float": csharpType = "double"; break;
                    case "image": csharpType = "byte[]"; break;
                    case "int": csharpType = "int"; break;
                    case "money": csharpType = "decimal"; break;
                    case "nchar": csharpType = "string"; break;
                    case "ntext": csharpType = "string"; break;
                    case "numeric": csharpType = "decimal"; break;
                    case "nvarchar": csharpType = "string"; break;
                    case "real": csharpType = "Single"; break;
                    case "smalldatetime": csharpType = "DateTime"; break;
                    case "smallint": csharpType = "short"; break;
                    case "smallmoney": csharpType = "decimal"; break;
                    case "sql_variant": csharpType = "object"; break;
                    case "sysname": csharpType = "object"; break;
                    case "text": csharpType = "string"; break;
                    case "time": csharpType = "TimeSpan"; break;
                    case "timestamp": csharpType = "byte[]"; break;
                    case "tinyint": csharpType = "byte"; break;
                    case "uniqueidentifier": csharpType = "Guid"; break;
                    case "varbinary": csharpType = "byte[]"; break;
                    case "varchar": csharpType = "string"; break;
                    case "xml": csharpType = "string"; break;
                    default: csharpType = "object"; break;
                }
                return csharpType;
            }
    
            public static Type MapCommonType(string dbtype)
            {
                if (string.IsNullOrEmpty(dbtype)) return Type.Missing.GetType();
                dbtype = dbtype.ToLower();
                Type commonType = typeof(object);
                switch (dbtype)
                {
                    case "bigint": commonType = typeof(long); break;
                    case "binary": commonType = typeof(byte[]); break;
                    case "bit": commonType = typeof(bool); break;
                    case "char": commonType = typeof(string); break;
                    case "date": commonType = typeof(DateTime); break;
                    case "datetime": commonType = typeof(DateTime); break;
                    case "datetime2": commonType = typeof(DateTime); break;
                    case "datetimeoffset": commonType = typeof(DateTimeOffset); break;
                    case "decimal": commonType = typeof(decimal); break;
                    case "float": commonType = typeof(double); break;
                    case "image": commonType = typeof(byte[]); break;
                    case "int": commonType = typeof(int); break;
                    case "money": commonType = typeof(decimal); break;
                    case "nchar": commonType = typeof(string); break;
                    case "ntext": commonType = typeof(string); break;
                    case "numeric": commonType = typeof(decimal); break;
                    case "nvarchar": commonType = typeof(string); break;
                    case "real": commonType = typeof(Single); break;
                    case "smalldatetime": commonType = typeof(DateTime); break;
                    case "smallint": commonType = typeof(short); break;
                    case "smallmoney": commonType = typeof(decimal); break;
                    case "sql_variant": commonType = typeof(object); break;
                    case "sysname": commonType = typeof(object); break;
                    case "text": commonType = typeof(string); break;
                    case "time": commonType = typeof(TimeSpan); break;
                    case "timestamp": commonType = typeof(byte[]); break;
                    case "tinyint": commonType = typeof(byte); break;
                    case "uniqueidentifier": commonType = typeof(Guid); break;
                    case "varbinary": commonType = typeof(byte[]); break;
                    case "varchar": commonType = typeof(string); break;
                    case "xml": commonType = typeof(string); break;
                    default: commonType = typeof(object); break;
                }
                return commonType;
            }
        }
    class config
        {
            
            //public static readonly string ConnectionString = "Data Source=.;Initial Catalog=WMBlogDB;User ID=sa;Password=666;";
            public static readonly string ConnectionString = File.ReadAllText(@"D:my-filedbCountPsw2.txt").Trim();
            public static readonly string DbDatabase = "";
            public static readonly string TableName = "";
        }#>

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

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

    填写代码

    //如果要获取主机信息,记得把 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,如果你连接数据库正确的话,会发现已经生成了全部实体类文件。

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

    //----------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 上获取即可,最终的效果是酱紫的:

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


     注意:上边是整个项目都生成出来了,也成功的产生了 work 文件夹下,看似一切很自然,但是不小心就遇到了其他的问题:

    我们打开 DbHelper.ttinclude 模板的最下边:

     public static readonly string ConnectionString = File.Exists(@"D:my-filedbCountPsw2.txt") ? File.ReadAllText(@"D:my-filedbCountPsw2.txt").Trim(): "server=.;uid=sa;pwd=sa;database=WMBlogDB";
     public static readonly string DbDatabase = "";
     public static readonly string TableName = "";//单独生成一个表

    就是上边,刚刚我们生成整个库的时候,这里是空的,如果现在填写了一个表明,比如 BlogArticle ,好吧,work 文件夹有了,但是没有生成指定的 cs 文件类,尴尬,然后就怀疑有 bug,

    这个时候请打开 Blog.Core.FrameWork.tt 研究下生成的逻辑代码,简单的 if else


    4、Oracle 的T4模板

    这里只有 Model 层的,其他的自行复制即可。

    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"#>
    
    <#+
    
    class Manager
    {
        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; }
    
        public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) {
            this.host = host;
            this.template = template;
            OutputPath = String.Empty;
            strategy = ManagementStrategy.Create(host);
        }
    
        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);
        }
    
        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;
                }
            }
        }
    }#>
    View Code

    生成 model 层 的代码

    感谢小伙伴 @博客园董事长的测试和指正,以下代码正确,可以成功导出 Oracle,

    <#@ template debug="true" hostspecific="true" language="C#" #>
    <#@ output extension=".cs" #>
    <#@ assembly name="System.Data" #>
    <#@ assembly name="System.Data.OracleClient" #>
    <#@ assembly name="System.Xml" #>
    <#@ import namespace="System" #>
    <#@ import namespace="System.Xml" #>
    <#@ import namespace="System.Data" #>
    <#@ import namespace="System.Data.OracleClient" #>
    <#@ import namespace="System.Collections.Generic"#>
    <#@ include file="$(ProjectDir)ModelAuto.ttinclude" #>
    <# var manager2 = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
    <# 
    ModelManager manager = new ModelManager();
    List<string> list=manager.GetTableList();
    #>
    
    <# 
        foreach (var item in list)
        {
            string tableName=item;
            DataTable table= manager.GetTableSchema(tableName);
     #>
    
     <# 
            manager2.StartBlock(tableName+".cs");
     #>
    using System;
    using System.Data;
    using System.Data.OracleClient;
    namespace Model
    {
        /// <summary>
        /// 数据表实体类:<#= tableName #> 
        /// </summary>
        [Serializable()]
        public class <#= tableName #>
        {
    <#
    foreach(DataRow row in table.Rows)
    {
    #>
           /// <summary>
           /// <#=row["备注"]#>
           /// </summary>     
           public <#= manager.TransFromSqlType(row["数据类型"].ToString())#> <#=row["字段名"]#>{ get; set; }
    <#}
    #>
        }
    }
    
    <# manager2.EndBlock(); #>
    
    <#
        }  
     #>     
       
    <# manager2.Process(true); #>
    
    <#+
        public class ModelManager
        {
            /// <summary>
            /// 数据库连接字符串
            /// </summary>
           private const string CONNECTION_STRING = "Data Source=orcl;Persist Security Info=True;User ID=jjmis;Password=jjmis;Unicode=True";
            /// <summary>
            /// 用户信息表名
            /// </summary>
            private const string PERSONINFO_TABLE_NAME = "USERINFO";
            /// <summary>
            /// 根据表名查询表结构信息
            /// </summary>
            private const string SELECT_SCHEMA_BY_TABLE_NAME = @"SELECT A.column_name    字段名,                                                                                           
                                                                        A.data_type      数据类型,
                                                                        A.data_length    长度,
                                                                        A.data_precision 整数位,
                                                                        A.Data_Scale     小数位,
                                                                        A.nullable       允许空值,
                                                                        A.Data_default   缺省值,
                                                                        B.comments       备注,
                                                                        A.TABLE_NAME     表名
                                                                        FROM user_tab_columns A, user_col_comments B
                                                                        WHERE a.COLUMN_NAME = b.column_name
                                                                        AND A.Table_Name = B.Table_Name
                                                                        AND A.Table_Name = '{0}'";
    
            /// <summary>
            /// 获得数据连接
            /// </summary>
            /// <returns></returns>
            private OracleConnection GetConnection()
            {
                return new OracleConnection(CONNECTION_STRING);
            }
    
            /// <summary>
            /// 得到当前用户的所有表名
            /// </summary>
            /// <returns></returns>
            public List<string> GetTableList()
            {
                string sql = "SELECT * FROM USER_TABLES";
                DataTable dt = OracleHelper.ExecuteDataTable(sql);
                List<string> list = new List<string>();
                if (dt!=null&&dt.Rows.Count>0)
                {
                    for (int i = 0; i < dt.Rows.Count; i++)
                    {
                        list.Add(dt.Rows[i]["TABLE_NAME"].ToString());
                    } 
                }
                return list;
             }
    
            /// <summary>
            /// 释放连接
            /// </summary>
            /// <param name="con"></param>
            private void ReleaseConnection(OracleConnection con)
            {
                if (con != null)
                {
                    if (con.State == ConnectionState.Open)
                    {
                        con.Close();
                    }
                }
            }
    
            public DataTable GetTableSchema(string tableName)
            {
                DataTable dt;
                using (OracleConnection con = GetConnection())
                {
                    con.Open();
                    OracleCommand cmd = con.CreateCommand();
                    cmd.CommandText = string.Format(SELECT_SCHEMA_BY_TABLE_NAME,tableName);
                    cmd.CommandType = CommandType.Text;
                    OracleDataAdapter adapter = new OracleDataAdapter(cmd);
                    DataSet ds = new DataSet(); 
                    adapter.Fill(ds);
                    dt = ds.Tables[0];
                }
    
                return dt;
            }
    
            /// <summary>
            /// SQL[不完善,需要的自己改造]
            /// </summary>
            /// <param name="type"></param>
            /// <returns></returns>
            public string TransFromSqlType(string type)
            {
                if (string.IsNullOrEmpty(type))
                {
                    return string.Empty;
                }
                if (string.Equals(type, "number", StringComparison.OrdinalIgnoreCase))
                {
                    return "int";
                }
                if (string.Equals(type, "date", StringComparison.OrdinalIgnoreCase))
                {
                    return "DateTime";
                }
                else if (string.Equals(type, "nvarchar2", StringComparison.OrdinalIgnoreCase))
                {
                    return "string";
                }
                return "string";
            }
        }
    #>
    
    <#+ 
    public class OracleHelper
        {
    
            private static string oracleConnectionStr = "Data Source=orcl;Persist Security Info=True;User ID=jjmis;Password=jjmis;Unicode=True";
            public static DataTable ExecuteDataTable(string sql, params OracleParameter[] paramList)
            {
                using (OracleConnection conn = new OracleConnection(oracleConnectionStr))
                {
                    conn.Open();
                    using (OracleCommand command = conn.CreateCommand())
                    {
                        command.CommandText = sql;
                        command.Parameters.AddRange(paramList);
                        DataTable dt = new DataTable();
                        OracleDataAdapter adapter = new OracleDataAdapter(command);
                        adapter.Fill(dt);
                        return dt;
                    }
                }
            }
    
            public static int ExecuteNonQuery(string sql, params OracleParameter[] paramList)
            {
                using (OracleConnection conn = new OracleConnection(oracleConnectionStr))
                {
                    conn.Open();
                    using (OracleCommand command = conn.CreateCommand())
                    {
                        command.CommandText = sql;
                        command.Parameters.AddRange(paramList);
                        return command.ExecuteNonQuery();
                    }
                }
            }
    
            public static object ExecuteScalar(string sql, params OracleParameter[] paramList)
            {
                using (OracleConnection conn = new OracleConnection(oracleConnectionStr))
                {
                    conn.Open();
                    using (OracleCommand command = conn.CreateCommand())
                    {
                        command.CommandText = sql;
                        command.Parameters.AddRange(paramList);
                        return command.ExecuteScalar();
                    }
                }
            }
        }
    
     #>
    View Code

    5、Mysql 的T4模板

    感谢 @hktkryg 和 @吉喆吉 的意见和建议:

    1、如果MySQL要生成大写的话,需要在my.ini中添加 lower_case_table_names=2,让mysql区分大小写,生成出来的就跟所设置的表名,字段名一样了,默认mysql在windows下不区分大小写。

    2、可以看看下边评论区 吉喆吉 博友的意见。

    对应的 Mysql 的 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;
                }
            }
        }
    }#>
    ModelAuto.ttinclude

    对应的 Mysql 的 MySqlHelper.ttinclude 内容:

    <#+
    public class EntityHelper
        {
            public static List<Entity> GetEntities(string connectionString, List<string> databases)
            {
                var list = new List<Entity>();
                var conn = new MySqlConnection(connectionString);
                try
                {
                    conn.Open();
                    var dbs = string.Join("','", databases.ToArray());
                    var cmd = string.Format(@"SELECT `information_schema`.`COLUMNS`.`TABLE_SCHEMA`
                                                        ,`information_schema`.`COLUMNS`.`TABLE_NAME`
                                                        ,`information_schema`.`COLUMNS`.`COLUMN_NAME`
                                                        ,`information_schema`.`COLUMNS`.`DATA_TYPE`
                                                        ,`information_schema`.`COLUMNS`.`COLUMN_COMMENT`
                                                    FROM `information_schema`.`COLUMNS`
                                                    WHERE `information_schema`.`COLUMNS`.`TABLE_SCHEMA` IN ('{0}') ", dbs);
                    using (var reader = MySqlHelper.ExecuteReader(conn, cmd))
                    {
                        while (reader.Read())
                        {
                            var db = reader["TABLE_SCHEMA"].ToString();
                            var table = reader["TABLE_NAME"].ToString();
                            var column = reader["COLUMN_NAME"].ToString();
                            var type =  reader["DATA_TYPE"].ToString();
                            var comment = reader["COLUMN_COMMENT"].ToString();
                            var entity = list.FirstOrDefault(x => x.EntityName == table);
                            if(entity == null)
                            {
                                entity = new Entity(table);
                                entity.Fields.Add(new Field
                                {
                                    Name = column,
                                    Type = GetCLRType(type),
                                    Comment = comment
                                });
                               
                                list.Add(entity);
                            }
                            else
                            {
                                entity.Fields.Add(new Field
                                {
                                    Name = column,
                                    Type = GetCLRType(type),
                                    Comment = comment
                                });
                            }
                        }
                    }
                }
                finally
                {
                    conn.Close();
                }
    
                return list;
            }
    
            public static string GetCLRType(string dbType)
            {
                switch(dbType)
                {
                    case "tinyint":
                    case "smallint":
                    case "mediumint":
                    case "int":
                    case "integer":
                        return "int";
                    case "double":
                        return "double";
                    case "float":
                        return "float";
                    case "decimal":
                        return "decimal";
                    case "numeric":
                    case "real":
                        return "decimal";
                    case "bit":
                        return "bool";
                    case "date":
                    case "time":
                    case "year":
                    case "datetime":
                    case "timestamp":
                        return "DateTime";
                    case "tinyblob":
                    case "blob":
                    case "mediumblob":
                    case "longblog":
                    case "binary":
                    case "varbinary":
                        return "byte[]";
                    case "char":
                    case "varchar":                   
                    case "tinytext":
                    case "text":
                    case "mediumtext":
                    case "longtext":
                        return "string";
                    case "point":
                    case "linestring":
                    case "polygon":
                    case "geometry":
                    case "multipoint":
                    case "multilinestring":
                    case "multipolygon":
                    case "geometrycollection":
                    case "enum":
                    case "set":
                    default:
                        return dbType;
                }
            }
        }
    
        public class Entity
        {
            public Entity()
            {
                this.Fields = new List<Field>();
            }
    
            public Entity(string name)
                : this()
            {
                this.EntityName = name;
            }
    
            public string EntityName { get;set; }
            public List<Field> Fields { get;set; }
        }
    
        public class Field
        {
            public string Name { get;set; }
            public string Type { get;set; }
            public string Comment { get;set; }
        }
    
    class config
        {
            
            public static readonly string ConnectionString = "Database=appsoft;Data Source=127.0.0.1;User Id=root;Password=123456;pooling=false;CharSet=utf8;port=3306";
            public static readonly string DbDatabase = "";
            public static readonly string TableName = "";
            public static readonly string ModelNameSpace = "App.Entities";
            public static readonly string IRepositoryNameSpace = "App.IRepository";
            public static readonly string RepositoryNameSpace = "App.Repository";
            public static readonly string IServicesNameSpace = "App.IServices";
            public static readonly string ServicesNameSpace = "App.Services";
        }#>
    MySqlHelper.ttinclude

    具体的Demo项目详情,可以下载:https://files.cnblogs.com/files/laozhang-is-phi/App.T4.MySql.rar

     注意事项:
    1、<#@ assembly name="C:Usersyouga.nugetpackagesmysql.data8.0.14lib et452MySql.Data.dll" #>  修改为你的引用;
    2、MySqlHelper.ttinclude 修改ConnectionString变量;
    3、App.Entities.tt 修改connectionString变量;
    4、可以根据自己的需求修改T4模板;
    5、生成的类名都是小写,可否转化为帕斯卡命名规范(待解决); 

    四、SqlSugar等ORM框架来实现 DbFirst

     一般的ORM都会有这个功能,无论是Code First ,还是根据数据库DB来生成 .cs 实体类,我的项目用的是Sqlsugar,所以也封装了响应的方法,

    具体代码,可以参考 blog.core层下的 Controllers / DbFirst / DbFirstController.cs 文件,里边有对Repository、IRepository、Service、IService、Model层的自动生成接口,

    代码很简单,看看就行了。

    五、结语

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

    感谢群内大神提供开源资料:

    通过Visual Studio扩展开发实现领域驱动开发

    VisualStudio 扩展开发

    Resharper 自定义代码片

    六、Github代码

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

  • 相关阅读:
    R
    P
    O
    M
    二分算法的一些思考
    I
    H
    G
    5-46 新浪微博热门话题 (30分)——unfinished HASH
    BZOJ 1179: [Apio2009]Atm
  • 原文地址:https://www.cnblogs.com/laozhang-is-phi/p/9757999.html
Copyright © 2020-2023  润新知