注:本文系作者原创,但可随意转载。

  现在呆的公司使用的数据库几乎都是MySQL。编程方式DatabaseFirst。即先写数据库设计,表设计按照规范好的文档写进EXCEL里,然后用公司的宏,生成建表脚本和实体类文件。

  之前就见识过T4模板生成SQL实体类文件,但还没自己实践过,这次正好实现一下生成MySQL的实体类。

目标类文件结构大致如下:

 //-----------------------------------------------------------------------
// <copyright file=" UserProfile2.cs" company="xxx Enterprises">
// * Copyright (C) 2015 xxx Enterprises All Rights Reserved
// * version : 4.0.30319.18444
// * author : auto generated by T4
// * FileName: UserProfile2.cs
// * history : Created by T4 11/24/2015 18:05:30
// </copyright>
//-----------------------------------------------------------------------
using System; namespace Console4Test
{
/// <summary>
/// UserProfile2 Entity Model
/// </summary>
[Serializable]
public class UserProfile2
{
/// <summary>
/// 主键ID
/// </summary>
public string ID { get; set; } /// <summary>
/// 姓名
/// </summary>
public string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
public int Age { get; set; } /// <summary>
/// 性别
/// </summary>
public int Gender { get; set; }
}
}

UserProfile2

  主要思路其实就两步:

    1)读取数据库表结构信息。(视个人情况,读取到的信息够用即可。)  

    2)根据读取到的表结构信息,为每个表生成实体类文件。

  在实现第一步时,参考了一些SQL的文章。很多是需要多次执行SQL,感觉有点儿浪费。看了下MySQL的系统库information_schema,里面有张COLUMNS表,表里有TABLE_SCHEMA(即数据库名), TABLE_NAME(表名),  COLUMN_NAME(列名),  DATA_TYPE(数据类型), COLUMN_COMMENT(列说明)等字段,已能满足基本需求,因此读库时,只进行一次查询即可。

  下面列出Helper的代码,只有2个方法,一是负责读取数据库表结构,二是把MySql数据库类型与C#数据类型匹配,这里我们建表时不允许为NULL,所以也不存在匹配可空类型,比较简单。可能有的匹配的不对,我没有全部试验过,一些特殊类型比如set, enum等直接返回类型字符串,不做处理,让编译报错即可。

 <#@ assembly name="System.Core"#>
<#@ assembly name="System.Data"#>
<#@ assembly name="$(ProjectDir)\PublicDll\MySql.Data.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="MySql.Data.MySqlClient" #>
<#+
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; }
}
#>

EntityHelper

  这里需要注意的大概有三点:

    1)我通过NuGet引用的MySQL.Data.dll直接引用报错找不到文件,我把它拷贝到PublicDLL\文件夹下进行引用。

    2)此文件为模板执行时引用的文件,不需直接执行,因此将其后缀名改为.ttinclude。

    3)MySQL在Windows下安装后默认表名等大小写不敏感。比如UserProfile表,读出来就是userprofile,这样生成的类名就是userprofile。因此需要对MySQL进行配置使其对大小写敏感。很简单可自行百度。

  

  第一步实现后,我捣鼓了两下后发现执行模板只能生成一个文件,看的示例也比较简单,没有说生成多个文件的。后来搜索了一下,引用一个老外写的Helper类就可以了,这个方法应该比较流行吧,看了下比较简单,试了下也可以就没看别的方法。

  附上他的博客地址:http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited

  下面附上他的Helper类代码:

  

 <#@ assembly name="System.Core"#>
<#@ assembly name="System.Data.Linq"#>
<#@ assembly name="EnvDTE"#>
<#@ assembly name="System.Xml"#>
<#@ assembly name="System.Xml.Linq"#>
<#@ import namespace="System"#>
<#@ import namespace="System.CodeDom"#>
<#@ import namespace="System.CodeDom.Compiler"#>
<#@ import namespace="System.Collections.Generic"#>
<#@ import namespace="System.Data.Linq"#>
<#@ import namespace="System.Data.Linq.Mapping"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="System.Linq"#>
<#@ import namespace="System.Reflection"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="System.Xml.Linq"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
<#+
 
// Manager class records the various blocks so it can split them up
class Manager {
    private class Block {
        public String Name;
        public int Start, Length;
    }
 
    private Block currentBlock;
    private List<Block> files = new List<Block>();
    private Block footer = new Block();
    private Block header = new Block();
    private ITextTemplatingEngineHost host;
    private StringBuilder template;
    protected List<String> generatedFileNames = new List<String>();
 
    public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
        return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
    }
 
    public void StartNewFile(String name) {
        if (name == null)
            throw new ArgumentNullException("name");
        CurrentBlock = new Block { Name = name };
    }
 
    public void StartFooter() {
        CurrentBlock = footer;
    }
 
    public void StartHeader() {
        CurrentBlock = header;
    }
 
    public void EndBlock() {
        if (CurrentBlock == null)
            return;
        CurrentBlock.Length = template.Length - CurrentBlock.Start;
        if (CurrentBlock != header && CurrentBlock != footer)
            files.Add(CurrentBlock);
        currentBlock = null;
    }
 
    public virtual void Process(bool split) {
        if (split) {
            EndBlock();
            String headerText = template.ToString(header.Start, header.Length);
            String footerText = template.ToString(footer.Start, footer.Length);
            String outputPath = Path.GetDirectoryName(host.TemplateFile);
            files.Reverse();
            foreach(Block block in files) {
                String fileName = Path.Combine(outputPath, block.Name);
                String content = headerText + template.ToString(block.Start, block.Length) + footerText;
                generatedFileNames.Add(fileName);
                CreateFile(fileName, content);
                template.Remove(block.Start, block.Length);
            }
        }
    }
 
    protected virtual void CreateFile(String fileName, String content) {
        if (IsFileContentDifferent(fileName, content))
            File.WriteAllText(fileName, content);
    }
 
    public virtual String GetCustomToolNamespace(String fileName) {
        return null;
    }
 
    public virtual String DefaultProjectNamespace {
        get { return null; }
    }
 
    protected bool IsFileContentDifferent(String fileName, String newContent) {
        return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
    }
 
    private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
        this.host = host;
        this.template = template;
    }
 
    private Block CurrentBlock {
        get { return currentBlock; }
        set {
            if (CurrentBlock != null)
                EndBlock();
            if (value != null)
                value.Start = template.Length;
            currentBlock = value;
        }
    }
 
    private class VSManager: Manager {
        private EnvDTE.ProjectItem templateProjectItem;
        private EnvDTE.DTE dte;
        private Action<String> checkOutAction;
        private Action<IEnumerable<String>> projectSyncAction;
 
        public override String DefaultProjectNamespace {
            get {
                return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
            }
        }
 
        public override String GetCustomToolNamespace(string fileName) {
            return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
        }
 
        public override void Process(bool split) {
            if (templateProjectItem.ProjectItems == null)
                return;
            base.Process(split);
            projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
        }
 
        protected override void CreateFile(String fileName, String content) {
            if (IsFileContentDifferent(fileName, content)) {
                CheckoutFileIfRequired(fileName);
                File.WriteAllText(fileName, content);
            }
        }
 
        internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
            : base(host, template) {
            var hostServiceProvider = (IServiceProvider) host;
            if (hostServiceProvider == null)
                throw new ArgumentNullException("Could not obtain IServiceProvider");
            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);
            checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
            projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
        }
 
        private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
            var keepFileNameSet = new HashSet<String>(keepFileNames);
            var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
            var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames()) + ".";
            foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
                projectFiles.Add(projectItem.get_FileNames(), projectItem);
 
            // Remove unused items from the project
            foreach(var pair in projectFiles)
                if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
                    pair.Value.Delete();
 
            // Add missing files to the project
            foreach(String fileName in keepFileNameSet)
                if (!projectFiles.ContainsKey(fileName))
                    templateProjectItem.ProjectItems.AddFromFile(fileName);
        }
 
        private void CheckoutFileIfRequired(String fileName) {
            var sc = dte.SourceControl;
            if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
                checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
        }
    }
} #>

T4Manager

  同样把这个Helper类的后缀名改为.ttinclude

  需要注意的是这个文件引用了EnvDTE,看了下好像是操作VS用的,写VS插件什么的应该会用到吧。可直接从.net框架引用。但后来我把这个引用移除了好像也没什么影响。

最后贴上,我们用来执行的模板

 <#@ template debug="false" hostspecific="true" language="C#" #>
<#@ include file="Manager.ttinclude" #>
<#@ include file="EntityHelper.ttinclude" #>
<#
// 是否是WCF服务模型
bool serviceModel = false; // 数据库连接
var connectionString = @"server=127.0.0.1;uid=root;pwd=12345678;charset=utf8;"; // 需要解析的数据库
var database = new List<string> { "chatroom" }; // 文件版权信息
var copyright = DateTime.Now.Year + " xxxx Enterprises All Rights Reserved";
var version = Environment.Version;
var author = "auto generated by T4"; var manager = Manager.Create(Host, GenerationEnvironment);
var entities = EntityHelper.GetEntities(connectionString, database); foreach(Entity entity in entities)
{
manager.StartNewFile(entity.EntityName + ".cs");
#>
//-----------------------------------------------------------------------
// <copyright file=" <#= entity.EntityName #>.cs" company="xxxx Enterprises">
// * Copyright (C) <#= copyright #>
// * version : <#= version #>
// * author : <#= author #>
// * FileName: <#= entity.EntityName #>.cs
// * history : Created by T4 <#= DateTime.Now #>
// </copyright>
//-----------------------------------------------------------------------
using System;
<# if(serviceModel)
{
#>
using System.Runtime.Serialization;
<#
}
#> namespace Console4Test
{
/// <summary>
/// <#= entity.EntityName #> Entity Model
/// </summary>
[Serializable]
<# if(serviceModel)
{
#>
[DataContract]
<#
}
#>
public class <#= entity.EntityName #>
{
<#
for(int i = ; i < entity.Fields.Count; i++)
{
if(i ==){
#> /// <summary>
/// <#= entity.Fields[i].Comment #>
/// </summary>
<# if(serviceModel)
{
#>
[DataMember]
<#
}
#>
public <#= entity.Fields[i].Type #> <#= entity.Fields[i].Name #> { get; set; }
<#
}
else{
#>
/// <summary>
/// <#= entity.Fields[i].Comment #>
/// </summary>
<# if(serviceModel)
{
#>
[DataMember]
<#
}
#>
public <#= entity.Fields[i].Type #> <#= entity.Fields[i].Name #> { get; set; }
<# }
}
#>
}
}
<#
manager.EndBlock();
} manager.Process(true);
#>

TextTemplate

  至此,已基本实现。在需要执行的模板里按下Ctrl+S,它就会执行一遍。

  里面有些写死的东西,可以调整到配置文件或其他地方。比如是否是WCF模型,如果是的话会自动加上[DataMember]等属性。具体格式等可自行扩展。

  

  

使用T4模板生成MySql数据库实体类的更多相关文章

  1. C# T4 模板 数据库实体类生成模板(带注释,娱乐用)

     说明:..,有些工具生成实体类没注释,不能和SqlServer的MS_Description属性一起使用,然后照着网上的资源,随便写了个生成模板,自娱自乐向,其实卵用都没有参考教程    1.htt ...

  2. T4教程2 T4模版引擎之生成数据库实体类

    T4模版引擎之生成数据库实体类   在通过T4模版引擎之基础入门 对T4有了初步印象后,我们开始实战篇.T4模板引擎可以当做一个代码生成器,代码生成器的职责当然是用来生成代码(这不是废话吗).而这其中 ...

  3. [转]T4模版引擎之生成数据库实体类

    本文转自:http://www.cnblogs.com/lzrabbit/archive/2012/07/18/2597953.html 在通过T4模版引擎之基础入门 对T4有了初步印象后,我们开始实 ...

  4. T4模版引擎之生成数据库实体类

    在通过T4模版引擎之基础入门 对T4有了初步印象后,我们开始实战篇.T4模板引擎可以当做一个代码生成器,代码生成器的职责当然是用来生成代码(这不是废话吗).而这其中我们使用的最普遍的是根据数据库生成实 ...

  5. c#实例化继承类,必须对被继承类的程序集做引用 .net core Redis分布式缓存客户端实现逻辑分析及示例demo 数据库笔记之索引和事务 centos 7下安装python 3.6笔记 你大波哥~ C#开源框架(转载) JSON C# Class Generator ---由json字符串生成C#实体类的工具

    c#实例化继承类,必须对被继承类的程序集做引用   0x00 问题 类型“Model.NewModel”在未被引用的程序集中定义.必须添加对程序集“Model, Version=1.0.0.0, Cu ...

  6. Visual Studio 2013 EF5实体数据模型 EDMX 使用 T4模板生成后使用 ObjectContext对象

    Visual Studio 2013 EF5实体数据模型 EDMX 使用 T4模板生成后的继承对象为DbContext,以前的熟悉的ObjectContext对象不见了,当然使用ObjectConte ...

  7. 【EF框架】EF DBFirst 快速生成数据库实体类 Database1.tt

    现有如下需求,数据库表快速映射到数据库实体类 VS给出的两个选择都有问题,并不能实现,都是坑啊 EF .x DbContext 生成器 EF .x DbContext 生成器 测试结果如下 生成文件 ...

  8. 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)

    一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...

  9. SqlServer数据库表生成C# Model实体类SQL语句——补充

    在sql语句最前边加上  use[数据库名] 原链接:https://www.cnblogs.com/jhli/p/11552105.html   --[SQL骚操作]SqlServer数据库表生成C ...

随机推荐

  1. Python3爬虫(九) 数据存储之关系型数据库MySQL

    Infi-chu: http://www.cnblogs.com/Infi-chu/ 关系型数据库关系型数据库是基于关系模型的数据库,而关系模型是通过二维表来保存的,所以关系型数据库的存储方式就是行列 ...

  2. python基础集结号

    Python 号称是最接近人工智能的语言,因为它的动态便捷性和灵活的三方扩展,成就了它在人工智能领域的丰碑 走进Python,靠近人工智能 一.编程语言Python的基础 之 "浅入浅出&q ...

  3. git初始化仓库相关

    当我们需要新建一个git项目会遇到的问题 全局设置 git config --global user.name "名字" git config --global user.emai ...

  4. 3 python3 编码解码问题 upd接受数据

    1.python3下的中文乱码:send_data.encode("utf-8") from socket import * udp_socket = socket(AF_INET ...

  5. 删除txt文件内容

    删除txt文件里的 聊天记录的时间那一行 f = open("d:\\面试.txt", "r") g = open("d:\\英雄联盟ADC技巧.tx ...

  6. C++调用Asprise OCR识别图片

    在一个识别软件中发现了Asprise OCR的"身影",上官网查了一下相关信息,发现功能挺强大的,识别印刷体应该不错,遗憾的是好像不能识别中文,不过不知道它对扭曲后的英文识别能力怎 ...

  7. MySQL高可用之PXC安装部署

      Preface       Today,I'm gonna implement a PXC,Let's see the procedure.   Framework   Hostname IP P ...

  8. 「个人训练」Copying Books(UVa714)

    好久不更新主要是怠惰了....还要加强训练. 题意分析与思路 注意到这样一句话: our goal is to minimize the maximum number of pages assigne ...

  9. Python中send()和sendall()的区别

    Python中send()和sendall()的区别 估计每个学习Python网络编程的人,都会遇到过这样的问题: send()和sendall()到底有什么区别? send()和sendall()原 ...

  10. hdu 1556 Color the ball (区间更新 求某点值)

    Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a ...