- A+
一、前言
俗话说得好,懒是第一生产力。一个数据库表的字段太多,写的真是手抽筋。就想着怎么才能偷个懒。
如果你对T4基础语法不是很了解,可以参考我前面写的一篇文章 T4语法快速入门。
二、原理
我们要做的事情是通过数据库表生成实体类。
第一步 我们要查询出当前用户下的所有数据库表。
第二步 查询出数据库表的结构,比如字段的名称,字段的类型,字段的长度大小,是否为空等等。
三、新建DbHelper.ttinclude和ModelAuto.ttinclude
DbHelper.ttinclude
- <#+
- public class config
- {
- public static readonly string ConnectionString = "server=.;database=Test;uid=sa;pwd=123";
- public static readonly string DbDatabase = "";
- public static readonly string TableName = "";
- }
- #>
- <#+
- public class DbHelper
- {
- #region GetDbTables
- public static List<string> GetDbTablesNew(string connectionString, string database,string tables = null)
- {
- if (!string.IsNullOrEmpty(tables))
- {
- tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','"));
- }
- string sql = string.Format(@"SELECT
- obj.name tablename
- 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
- left join {0}.sys.extended_properties g ON (obj.object_id = g.major_id AND g.minor_id = 0 AND g.name= 'MS_Description')
- where type='U' {1}
- order by obj.name", database,tables);
- DataTable dt = GetDataTable(connectionString, sql);
- return dt.Rows.Cast<DataRow>().Select(row =>row.Field<string>("tablename")).ToList();
- }
- 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, 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"),
- Precision=row.Field<int>("Precision"),
- 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 GetPrimaryKey
- 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
- public static string StrToUpper(string Str)
- {
- string[] strArray = Str.ToLower().Split('_');
- string result = "";//定义一个空字符串
- foreach (string item in strArray)//循环处理数组里面每一个字符串
- {
- result += item.Substring(0, 1).ToUpper() + item.Substring(1);
- //.Substring(0, 1).ToUpper()把循环到的字符串第一个字母截取并转换为大写
- //并用s.Substring(1)得到循环到的字符串除第一个字符后的所有字符拼装到首字母后面。
- }
- return result;
- }
- }
- #region DbTable
- public sealed class DbTable
- {
- public string TableName { get; set; }
- public string SchemaName { get; set; }
- public int Rows { get; set; }
- public bool HasPrimaryKey { get; set; }
- }
- #endregion
- #region DbColumn
- public sealed class DbColumn
- {
- public int ColumnID { get; set; }
- public bool IsPrimaryKey { get; set; }
- public string ColumnName { get; set; }
- public string ColumnType { get; set; }
- public string CSharpType
- {
- get
- {
- return SqlServerDbTypeMap.MapCsharpType(ColumnType);
- }
- }
- /// <summary>
- ///
- /// </summary>
- public Type CommonType
- {
- get
- {
- return SqlServerDbTypeMap.MapCommonType(ColumnType);
- }
- }
- public int ByteLength { get; set; }
- public int CharLength { get; set; }
- public int Precision{get;set;}
- public int Scale { get; set; }
- public bool IsIdentity { get; set; }
- public bool IsNullable { get; set; }
- 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
- #>
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 int Start, Length;
- public String Name,OutputPath;
- }
- 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 Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) {
- this.host = host;
- this.template = template;
- strategy = ManagementStrategy.Create(host);
- }
- public void StartBlock(String name,String outputPath) {
- currentBlock = new Block { Name = name, Start = template.Length ,OutputPath=outputPath};
- }
- 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(block.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;
- }
- }
- }
- }#>
四、新建一个Model.tt
- <#@ template debug="true" hostspecific="true" language="C#" #>
- <#@ output extension=".cs" #>
- <#@ 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)/T4/DbHelper.ttinclude" #>
- <#@ include file="$(ProjectDir)/T4/ModelAuto.ttinclude" #>
- <# var manager = new Manager(Host, GenerationEnvironment, true); #>
- <#
- var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile);
- OutputPath1=Path.Combine(OutputPath1,"Template","Models");
- if (!Directory.Exists(OutputPath1))
- {
- Directory.CreateDirectory(OutputPath1);
- }
- #>
- <# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,""))
- {
- var tableName=DbHelper.StrToUpper(item);
- manager.StartBlock(tableName+".cs",OutputPath1);//文件名
- #>
- using System;
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- namespace CommonLibrary.Model
- {
- ///<summary>
- ///<#=tableName#>
- ///</summary>
- public class <#=tableName#>
- {
- <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#>
- /// <summary>
- /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n"," ") #>
- /// </summary>
- [Column("<#= column.ColumnName#>")]
- public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=DbHelper.StrToUpper(column.ColumnName)#> { get; set; }
- <# } #>
- }
- public class <#=tableName#>List
- {
- public List<<#=tableName#>> List { get; set; }
- public int? TotalSize { get; set; }
- }
- }
- <#
- manager.EndBlock();
- }
- manager.Process(true);
- #>
效果图
总结
好了,现在就可以自动生成实体类了。
T4不仅仅可以生成实体类 只要你够懒它就能帮你干更多的事