网上看到有人使用T4来生成代码,发现这个东西自己没有接触过,因此google了一下,学习了一下相关的知识。
现把自己学习的过程记录一下:
下面的代码主要是通过T4模板生成数据库表实体对象的过程。
1、首先我们需要实现 ITextTemplatingEngineHost 接口的类。该类存在"Microsoft.VisualStudio.TextTemplating.dll"中,因此我们需要在项目工程中引用此Dll文件。
下面是代码:
代码
[Serializable]
public class TableHost : Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost {
private string _namespace = "JXDModel";
public string Namespace {
get { return _namespace; }
set { _namespace = value; }
}
protected string _classname = "tablename";
public string Classname {
get { return _classname; }
set { _classname = value; }
}
protected DataTable _dt = new DataTable();
public DataTable Dt {
get { return _dt; }
set { _dt = value; }
}
protected string _templateFile = "";
public string TemplateFile {
get { return _templateFile; }
set { _templateFile = value; }
}
protected string _fileExtension = ".cs";
public string FileExtension {
get { return _fileExtension; }
set { _fileExtension = value; }
}
public IList<string> StandardAssemblyReferences {
get {
return new string[]
{
typeof(System.Uri).Assembly.Location,
typeof(TableHost).Assembly.Location,
typeof(DbType).Assembly.Location,
typeof(XmlDataDocument).Assembly.Location,
typeof(System.Xml.Serialization.XmlSchemas).Assembly.Location,
typeof(System.AppDomain ).Assembly.Location
};
}
}
public IList<string> StandardImports {
get {
return new string[]
{
"System",
"System.Data",
"System.IO",
"System.Xml",
"System.Xml.Serialization",
"System.Text",
"windows2008",
"System.Data.Common",
"System.Collections",
"System.Collections.Generic",
"System.Collections.Specialized"
};
}
}
public object GetHostOption(string optionName) {
object returnObject;
switch (optionName) {
case "CacheAssemblies":
returnObject = true;
break;
default:
returnObject = null;
break;
}
return returnObject;
}
public void SetFileExtension(string extension) {
_fileExtension = extension;
}
private System.Text.Encoding _fileEncodingValue = System.Text.Encoding.UTF8;
public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective) {
_fileEncodingValue = encoding;
}
private CompilerErrorCollection _errorsValue;
public CompilerErrorCollection Errors {
get { return _errorsValue; }
}
public void LogErrors(CompilerErrorCollection errors) {
_errorsValue = errors;
}
public string ResolvePath(string fileName) {
return fileName;
}
public bool LoadIncludeText(string requestFileName, out string content, out string location) {
content = System.String.Empty;
location = System.String.Empty;
//If the argument is the fully qualified path of an existing file,
//then we are done.
//----------------------------------------------------------------
if (File.Exists(requestFileName)) {
content = File.ReadAllText(requestFileName);
return true;
}
//This can be customized to search specific paths for the file.
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
else {
return false;
}
}
public string ResolveAssemblyReference(string assemblyReference) {
if (File.Exists(assemblyReference)) {
return assemblyReference;
}
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
if (File.Exists(candidate)) {
return candidate;
}
return "";
}
public Type ResolveDirectiveProcessor(string processorName) {
throw new Exception("Directive Processor not found");
}
public string ResolveParameterValue(string directiveId, string processorName, string parameterName) {
if (directiveId == null) {
throw new ArgumentNullException("the directiveId cannot be null");
}
if (processorName == null) {
throw new ArgumentNullException("the processorName cannot be null");
}
if (parameterName == null) {
throw new ArgumentNullException("the parameterName cannot be null");
}
//Code to provide "hard-coded" parameter values goes here.
//This code depends on the directive processors this host will interact with.
//If we cannot do better, return the empty string.
return String.Empty;
}
public AppDomain ProvideTemplatingAppDomain(string content) {
//This host will provide a new application domain each time the
//engine processes a text template.
//-------------------------------------------------------------
return AppDomain.CreateDomain("Generation App Domain");
//This could be changed to return the current appdomain, but new
//assemblies are loaded into this AppDomain on a regular basis.
//If the AppDomain lasts too long, it will grow indefintely,
//which might be regarded as a leak.
//This could be customized to cache the application domain for
//a certain number of text template generations (for example, 10).
//This could be customized based on the contents of the text
//template, which are provided as a parameter for that purpose.
}
}
public class TableHost : Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost {
private string _namespace = "JXDModel";
public string Namespace {
get { return _namespace; }
set { _namespace = value; }
}
protected string _classname = "tablename";
public string Classname {
get { return _classname; }
set { _classname = value; }
}
protected DataTable _dt = new DataTable();
public DataTable Dt {
get { return _dt; }
set { _dt = value; }
}
protected string _templateFile = "";
public string TemplateFile {
get { return _templateFile; }
set { _templateFile = value; }
}
protected string _fileExtension = ".cs";
public string FileExtension {
get { return _fileExtension; }
set { _fileExtension = value; }
}
public IList<string> StandardAssemblyReferences {
get {
return new string[]
{
typeof(System.Uri).Assembly.Location,
typeof(TableHost).Assembly.Location,
typeof(DbType).Assembly.Location,
typeof(XmlDataDocument).Assembly.Location,
typeof(System.Xml.Serialization.XmlSchemas).Assembly.Location,
typeof(System.AppDomain ).Assembly.Location
};
}
}
public IList<string> StandardImports {
get {
return new string[]
{
"System",
"System.Data",
"System.IO",
"System.Xml",
"System.Xml.Serialization",
"System.Text",
"windows2008",
"System.Data.Common",
"System.Collections",
"System.Collections.Generic",
"System.Collections.Specialized"
};
}
}
public object GetHostOption(string optionName) {
object returnObject;
switch (optionName) {
case "CacheAssemblies":
returnObject = true;
break;
default:
returnObject = null;
break;
}
return returnObject;
}
public void SetFileExtension(string extension) {
_fileExtension = extension;
}
private System.Text.Encoding _fileEncodingValue = System.Text.Encoding.UTF8;
public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective) {
_fileEncodingValue = encoding;
}
private CompilerErrorCollection _errorsValue;
public CompilerErrorCollection Errors {
get { return _errorsValue; }
}
public void LogErrors(CompilerErrorCollection errors) {
_errorsValue = errors;
}
public string ResolvePath(string fileName) {
return fileName;
}
public bool LoadIncludeText(string requestFileName, out string content, out string location) {
content = System.String.Empty;
location = System.String.Empty;
//If the argument is the fully qualified path of an existing file,
//then we are done.
//----------------------------------------------------------------
if (File.Exists(requestFileName)) {
content = File.ReadAllText(requestFileName);
return true;
}
//This can be customized to search specific paths for the file.
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
else {
return false;
}
}
public string ResolveAssemblyReference(string assemblyReference) {
if (File.Exists(assemblyReference)) {
return assemblyReference;
}
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
if (File.Exists(candidate)) {
return candidate;
}
return "";
}
public Type ResolveDirectiveProcessor(string processorName) {
throw new Exception("Directive Processor not found");
}
public string ResolveParameterValue(string directiveId, string processorName, string parameterName) {
if (directiveId == null) {
throw new ArgumentNullException("the directiveId cannot be null");
}
if (processorName == null) {
throw new ArgumentNullException("the processorName cannot be null");
}
if (parameterName == null) {
throw new ArgumentNullException("the parameterName cannot be null");
}
//Code to provide "hard-coded" parameter values goes here.
//This code depends on the directive processors this host will interact with.
//If we cannot do better, return the empty string.
return String.Empty;
}
public AppDomain ProvideTemplatingAppDomain(string content) {
//This host will provide a new application domain each time the
//engine processes a text template.
//-------------------------------------------------------------
return AppDomain.CreateDomain("Generation App Domain");
//This could be changed to return the current appdomain, but new
//assemblies are loaded into this AppDomain on a regular basis.
//If the AppDomain lasts too long, it will grow indefintely,
//which might be regarded as a leak.
//This could be customized to cache the application domain for
//a certain number of text template generations (for example, 10).
//This could be customized based on the contents of the text
//template, which are provided as a parameter for that purpose.
}
}
下载代码实例: /Files/james-dong/T42008.rar
2、编写生成对象实体的模板文件(.tt)
代码
<#@ Template language="C#" debug="true" hostspecific="true" #>
<#@ Assembly name = "System.Data" #>
<#@ Assembly name = "System.Xml" #>
<#@ import Namespace = "System.Data" #>
<#@ import Namespace = "System.Data.SqlClient" #>
<#@ import Namespace = "System.Xml" #>
<#@ output extension= ".cs" #>
using System;
using system.Data;
<#
TableHost host = (TableHost)(Host);
string namespacess = host.Namespace;
string classname = host.Classname;
DataTable dt = host.Dt;
#>
namespace <#=namespacess #>
{
public class <#= classname #>
{
<#
foreach( DataColumn dc in dt.Columns )
{
#>
private <#= dc.DataType.Name.ToString().ToLower() #> _<#= dc.ColumnName.ToString().ToLower() #>;
public <#= dc.DataType.Name.ToString().ToLower() #> <#= dc.ColumnName.ToString().ToUpper() #>
{
get{ return _<#= dc.ColumnName.ToString().ToLower()#>; }
set{ _<#=dc.ColumnName.ToString().ToLower()#>= value; }
}
<#
}
#>
}
}
<#@ Assembly name = "System.Data" #>
<#@ Assembly name = "System.Xml" #>
<#@ import Namespace = "System.Data" #>
<#@ import Namespace = "System.Data.SqlClient" #>
<#@ import Namespace = "System.Xml" #>
<#@ output extension= ".cs" #>
using System;
using system.Data;
<#
TableHost host = (TableHost)(Host);
string namespacess = host.Namespace;
string classname = host.Classname;
DataTable dt = host.Dt;
#>
namespace <#=namespacess #>
{
public class <#= classname #>
{
<#
foreach( DataColumn dc in dt.Columns )
{
#>
private <#= dc.DataType.Name.ToString().ToLower() #> _<#= dc.ColumnName.ToString().ToLower() #>;
public <#= dc.DataType.Name.ToString().ToLower() #> <#= dc.ColumnName.ToString().ToUpper() #>
{
get{ return _<#= dc.ColumnName.ToString().ToLower()#>; }
set{ _<#=dc.ColumnName.ToString().ToLower()#>= value; }
}
<#
}
#>
}
}
3、调用T4引擎生成实体过程
代码
public void Process(string namespace , string classname , string templatefile , string connstring ) {
TableHost host = new TableHost();
host.Namespace = namespace ;
host.Classname = classname ;
host.FileExtension = ".cs";
host.TemplateFile = templatefile;
SqlConnection conn = new SqlConnection(constring);
conn.Open();
DataTable dt = conn.GetSchema("tables");
string sql = "select * from " + host.Classname;
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter ad = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
ad.FillSchema(ds, SchemaType.Mapped, host.Classname );
host.Dt = ds.Tables[0];
Microsoft.VisualStudio.TextTemplating.Engine engine = new Microsoft.VisualStudio.TextTemplating.Engine();
string input = "";
string output = "";
input = System.IO.File.ReadAllText(host.TemplateFile);
output = engine.ProcessTemplate(input, host);
string outputname = host.Classname + host.FileExtension;
System.IO.File.WriteAllText(outputname , output);
foreach (CompilerError error in host.Errors) {
textBox1.Text += error.ToString();
}
TableHost host = new TableHost();
host.Namespace = namespace ;
host.Classname = classname ;
host.FileExtension = ".cs";
host.TemplateFile = templatefile;
SqlConnection conn = new SqlConnection(constring);
conn.Open();
DataTable dt = conn.GetSchema("tables");
string sql = "select * from " + host.Classname;
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter ad = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
ad.FillSchema(ds, SchemaType.Mapped, host.Classname );
host.Dt = ds.Tables[0];
Microsoft.VisualStudio.TextTemplating.Engine engine = new Microsoft.VisualStudio.TextTemplating.Engine();
string input = "";
string output = "";
input = System.IO.File.ReadAllText(host.TemplateFile);
output = engine.ProcessTemplate(input, host);
string outputname = host.Classname + host.FileExtension;
System.IO.File.WriteAllText(outputname , output);
foreach (CompilerError error in host.Errors) {
textBox1.Text += error.ToString();
}
到此,一个简单的数据库表实体类的功能就实现了。