ASP.NET MVC5+EF6+EasyUI 后台管理系统(29)-T4模版
本节不再适合本系统,在58,59节已经重构。请超过本节
这讲适合所有的MVC程序
很荣幸,我们的系统有了体验的地址了。演示地址
之前我们发布了一个简单的代码生成器,其原理就是读取数据库的表结构,生成文本的一个方式来生成代码!
为了替代重复的劳动,微软自己有一套T4模版,我不想把T4模版说得那么的复杂,因为这个复杂我自己也不知道。
原理跟市面的代码生成器一个道理,但是T4的扩展比代码生成器更灵活,可以更方便根据类生成代码等操作。
T4代码模式是没有颜色高亮了,但是我们的VS支持插件 T4 Editor
下载安装之后就可以跟我们写C#一样有智能高亮和提示了。
新建一个项目,专门用于T4的存放。

首先我们要让T4连接我们的数据库
新建一个DbHelper.ttinclude模版包含文件
<#+
public class DbHelper
{
#region GetDbTables 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 GetDbColumns 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, ) { 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 GetDataTable 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 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 SqlServerDbTypeMap 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"; 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;
}
}
#endregion #>
DbHelper.ttinclude
这里面包含了一些转换的方法和访问数据库的SQL语句
其中GetDbTables方法就是根据数据库的链接,表名来读取字段数据
怎么用呢?就是让模版包含这个文件,就可以访问方法了
举一个Model为例吧,新建Model文件夹。并新建文件,模版文件为tt结尾的扩展

我们新建Test.tt之后
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".txt" #>
最后一行<#@ output extension=".txt" #>
为模版生成的文件,可以是.cs可以是其他。我们就不改了,就是txt
如果是cs类,生成会编译,如果代码是对的,但是系统不知道是对的,会编译出错,虽然这不影响结果,但是不爽
我们为Test.tt添加如下代码
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.dll" #>
<#@ assembly name="System.Data.DataSetExtensions.dll" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ include file="$(ProjectDir)DbHelper.ttinclude" #>
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由T4模板自动生成
// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> by YmNets
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
我们引入了一些命名空间,因为我们要将读到的表结构进行一系列的处理,比如for循环等操作
其中<#@ include file="$(ProjectDir)DbHelper.ttinclude" #>就是包含了刚刚简历的访问数据库文件
T4模版的语法通过上面我们可以看出是<#@#>,<#=#>,<##>,<#+#>结构跟原生的asp.net一个鸟样,不是Razor语法,这里要注意
我们添加一些代码
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.dll" #>
<#@ assembly name="System.Data.DataSetExtensions.dll" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ include file="$(ProjectDir)DbHelper.ttinclude" #>
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由T4模板自动生成
// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> by YmNets
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.ComponentModel.DataAnnotations;
namespace <#=config.nameSpace#>
{ public class <#=config.TableName#>Model
{
<# 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; }
<#}#>
}
}
<#+
public class config
{
public static readonly string ConnectionString="Data Source=(local);Initial Catalog=AppDB;User ID=sa;Password=zhaoyun123!@#;";
public static readonly string DbDatabase="AppDB";
public static readonly string TableName="SysSample";
public static readonly string nameSpace="App.Sys";
}
#>
有一些通用的我建了一个类config,访问config只需要config.TableName即可
访问DbHelper.ttinclude,值需要DbHelper.***即可
所以同道理,通用的类我们可以建造 Common.ttinclude来完善模版生成库
OK。配置好之后,值需要展开Test.tt就可以看到Test.txt文件,文件生成情况如下
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由T4模板自动生成
// 生成时间 2014-03-03 17:34:45 by YmNets
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.ComponentModel.DataAnnotations;
namespace App.Sys
{ public class SysSampleModel
{
public string Id { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
public DateTime? Bir { get; set; }
public string Photo { get; set; }
public string Note { get; set; }
public DateTime? CreateTime { get; set; } }
}
非常快速,所以,你可以很快建立自己项目的模版文件了。
以上只是基础,我们更加关心的是更多的扩展,比如判断数据库类型啊等等,做逻辑处理,获取字段的长度做处理啊。
所以我给大家提供一个备注:下面这条语句可以得到表的结构
 <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, config.TableName)){#>
       <#}#> 
其中:
column.CSharpType 字段的类型
column.IsNullable 是否为空
column.IsPrimaryKey 是否是主键
column.CharLength 字段的长度,如varchar(50)那么column.CharLength=50
column.ColumnName 字段的名称
样例:判断是否DataTime类型
<#if(column.CSharpType=="DateTime"){#>
<# } #>
一个小小的例子,我们就能看懂MVC的T4模版,并能熟练运用它
ASP.NET MVC5+EF6+EasyUI 后台管理系统(29)-T4模版的更多相关文章
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(持续更新中...)
		
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 任务调度系统界面 http: ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(转)
		
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 日程管理 http://ww ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出-自定义表模导入
		
系列目录 前言 上一节使用了LinqToExcel和CloseXML对Excel表进行导入和导出的简单操作,大家可以跳转到上一节查看: ASP.NET MVC5+EF6+EasyUI 后台管理系统(6 ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统-WebApi的用法与调试
		
1:ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-WebApi与Unity注入 使用Unity是为了使用我们后台的BLL和DAL层 2:ASP.NET MVC5+EF6+Easy ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(51)-系统升级
		
系统很久没有更新内容了,期待已久的更新在今天发布了,最近花了2个月的时间每天一点点,从原有系统 MVC4+EF5+UNITY2.X+Quartz 2.0+easyui 1.3.4无缝接入 MVC5+E ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(58)-DAL层重构
		
系列目录 前言:这是对本文系统一次重要的革新,很久就想要重构数据访问层了,数据访问层重复代码太多.主要集中增删该查每个模块都有,所以本次是为封装相同接口方法 如果你想了解怎么重构普通的接口DAL层请查 ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(34)-文章发布系统①-简要分析
		
系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(1)-前言与,虽然有点没有目的的学习,但还是了解了Andro ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
		
系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...
 - ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox
		
系列目录 https://yunpan.cn/cZVeSJ33XSHKZ 访问密码 0fc2 今天整合lightbox插件Fancybox1.3.4,发现1.3.4版本太老了.而目前easyui 1 ...
 
随机推荐
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
			
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
 - 从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群)
			
从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
 - React在开发中的常用结构以及功能详解
			
一.React什么算法,什么虚拟DOM,什么核心内容网上一大堆,请自行google. 但是能把算法说清楚,虚拟DOM说清楚的聊聊无几.对开发又没卵用,还不如来点干货看看咋用. 二.结构如下: impo ...
 - PowerShell过滤文件中的重复内容
			
Get-Content -Path E:\test11\data.txt | Sort-Object | Get-Unique 源文件: AA0001 2014-06-30 15:27:13.073 ...
 - .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整(续)-使用配置文件动态注入
			
上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全:另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃肿.理想的方式是,w ...
 - 简单搭建 nuget 内部服务器
			
搭建 nuget 内部服务器,最好的方式是使用 ProGet,参考博文<用 ProGet 搭建内部的 NuGet 服务器>,好处非常多,但需要使用 SQL Server 数据库,如果不想使 ...
 - AFNetworking 3.0 源码解读(八)之 AFImageDownloader
			
AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...
 - 用原生js做单页应用
			
最近在公司接到一个需求,里面有一个三级跳转.类似于选择地址的时候,选择的顺序是:省份->市->区.如果分三个页面跳转,那么体验非常不好,如果引入其他框架做成单页应用,又比较麻烦.所以可以用 ...
 - 【Java每日一题】20170104
			
20170103问题解析请点击今日问题下方的"[Java每日一题]20170104"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...
 - javascript  笔记!
			
1.通过javascript向文档中输出文本 document是javascript的内置对象,代表浏览器的文档部分 document.write("Hello Javascript&quo ...