缘起
哈喽大家周二好呀,这个国庆过的真是懒洋洋呀,不知道大家的学习动力咋样了,刚一上班本人手中的项目也增加了,但是还是要抽出时间学习哒,为了不让老板大大天天催,所以更新会慢点儿 [ 哭笑 ] :bowtie:,不过在我的推荐下,公司下一个项目要我负责前后端分离,终于可以将这些派上用场了,中间的坑也会在以后的文章中,慢慢补充出来。
这几天简单想了想,还没有想好要开什么系列,就想到QQ群里有小伙伴问的较多的一些问题以及前边系列文章中的提到的,但是没有深入讲到的,再填一下坑,这样才是完整的嘛,大家要是看到之前的有任何不清楚的,或者想扩展的,可以在群里说一下,或者留言,我都会说到的。今天呢,就说说如果半自动化搭建项目,这个坑来自于之前的项目搭建文章《框架之六 || API项目整体搭建 6.1 仓储模式》的伏笔之一 —— 如何使用 T4 模板搭建项目。当然,我们不仅是简单说说实体类的创建,也会横向和纵向的讨论下,一个项目如何通过代码实现半自动化搭建。
说到这里大家应该已经明白了这篇文章的写作意图,就是搭建项目的问题,相信大家已经开发了几年了,都有自己的一套行之有效的办法或者是经验,当然这里就不说付费的了,付费的框架何其多,比如迪西客,普元或者力软等等,这都是好用的功能强大的付费框架,既然说技术,就不说付费的,咱们自己写,这里简单概括下今天要说到的:
今天咱们这几个方法都会说到,主要还是说一下通过 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
截图
打开看一下,基本的 CURD 方法都有,也有一些存储过程的调用等,而且可以自定义扩展,不得不说是一个神器,如果要开发 ADO.NET 多层,这个方法也不失为一个好的方案。
优点大家都看到了,快速,内容简洁丰富,各种存储过程分页方法很方便,不言而喻;
但是缺点也是有的,最大的就是不能像其他ORM框架那样,实现 Code First ,只能单纯的讲数据库信息提取出来;而且在编辑模板方面也没有 T4 那么容易操作,修改模板代码不是很方便,学习的难度稍微大了一些。
总结:如果不想用 T4 模板手动写模板代码,又不想引入框架,只想用 ADO.NET 搭建多层框架,动软代码生成器是一个不错的选择。
二、通过 VSTO 编写 Excel 来搭建整体项目框架
1、什么是VSTO?
2、如何新建一个 VSTO 项目
网上有一个栗子,大家可以参看着自己玩一玩:https://www.cnblogs.com/crhdyl/p/5289307.html。 对于这个方法,大家自己玩一玩就好了,这里就不说具体的教程了,毕竟不是本文重点。
QQ群里有一个小伙伴大神,他通过 VSTO 来写的一个框架,其实就是通过命令来将固定的格式进行输出,这里盗用一下他的图,应该还好吧,因为不知道他的博客园账号,就先不@他了,个人感觉这个还是很不错的,看着很溜
这个方法我现在也在学,不过只是做一个知识扩展来用,好处是,我们自己做一个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,用于单独存放我们的模板以及生成的实体类文件
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 模板,主要是数据库操作类
<#+//定义数据库帮助类 public 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 } #region DbTable /// <summary> /// 表结构 /// </summary> public sealed 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; } } #endregion #region DbColumn /// <summary> /// 表字段结构 /// </summary> public sealed 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; } } #endregion #region 数据库和c#的数据类型map public 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