T4模板使用心得

TextTemplatingFilePreprocessor 和TextTemplatingFileGenerator 的区别
TextTemplatingFilePreprocessor 是运行时模板(run-time),在模板保存时生成的是根据模板生成的cs代码,在客户端进行调用才能生成想要的文件。
TextTemplatingFileGenerator 是设计时模板 design-time,保存模板之后,就直接生成想要的代码。

**TextTemplatingFileGenerator 在项目中的应用**
创建一个帮助类,从DbContext中读取实体类名。

```

public class TemplateUtil
{
public class TemplateName
{
public string ModelName { get; set; }
public string EntityName { get; set; }
}
public static List<TemplateName> GetModelNames()
{
List<TemplateName> ModelNames = new List<TemplateName>();
Type t = new DbContext().GetType();
PropertyInfo[] PropertyList = t.GetProperties();
foreach (PropertyInfo item in PropertyList)
{
if (item.Name != "Database" && item.Name != "ChangeTracker"&&item.Name != "Configuration"&&item.Name!= "SysAccountRoles"&&item.Name!= "SysRoleMenus"&&item.Name!= "SysRoleFunctions")
{
ModelNames.Add(new TemplateName { ModelName= TemplateUtil.ToSingular(item.Name) ,EntityName=item.Name});
}

}
return ModelNames;
}


        /// <summary>
        /// 单词变成单数形式
        /// </summary>
        /// <param name="word"></param>
        /// <returns></returns>
        public static string ToSingular(string word)
{
Regex plural1 = new Regex("(?<keep>[^aeiou])ies$");
Regex plural2 = new Regex("(?<keep>[aeiou]y)s$");
Regex plural3 = new Regex("(?<keep>[sxzh])es$");
Regex plural4 = new Regex("(?<keep>[^sxzhyu])s$");
Regex plural5= new Regex("(?<keep>[u])s$");
if (word.ToLower() == "news")
return word;
else if(plural1.IsMatch(word))
return plural1.Replace(word, "${keep}y");
else if (plural2.IsMatch(word))
return plural2.Replace(word, "${keep}");
else if (plural3.IsMatch(word))
return plural3.Replace(word, "${keep}");
else if (plural4.IsMatch(word))
return plural4.Replace(word, "${keep}");
else if (plural5.IsMatch(word))
return plural5.Replace(word, "${keep}");
return word;
}
        /// <summary>
        /// 单词变成复数形式
        /// </summary>
        /// <param name="word"></param>
        /// <returns></returns>
        public static string ToPlural(string word)
{
Regex plural1 = new Regex("(?<keep>[^aeiou])y$");
Regex plural2 = new Regex("(?<keep>[aeiou]y)$");
Regex plural3 = new Regex("(?<keep>[sxzh])$");
Regex plural4 = new Regex("(?<keep>[^sxzhy])$");

if (plural1.IsMatch(word))
return plural1.Replace(word, "${keep}ies");
else if (plural2.IsMatch(word))
return plural2.Replace(word, "${keep}s");
else if (plural3.IsMatch(word))
return plural3.Replace(word, "${keep}es");
else if (plural4.IsMatch(word))
return plural4.Replace(word, "${keep}s");

return word;
}
}

```
从DbContext中读取DbSet属性,获取全部的实体。
创建一个tangible t4 blank模板 ISvcTemplate.tt


```

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Windows.Forms" #>
<#@ Assembly Name="$(TargetDir)TemplateUtil.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>


using System.Collections.Generic;
using Models;
using Models.DTOS;
using Tools;
<#List<TemplateUtil.TemplateName> ModelNames=TemplateUtil.GetModelNames();
foreach (var item in ModelNames)
{#>


namespace Services.Template
{
public interface I<#=item.ModelName#>Svc
{
#region I<#=item.ModelName#>Svc
/// <summary>
/// 获取模型
/// </summary>
/// <param name="id">主键</param>
/// <returns></returns>
<#=item.ModelName#> GetModel(int? id);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="pager">分页model</param>
/// <param name="search">查询model</param>
/// <returns></returns>
object GetPager(GridPageModel pager, SearchModel search);
/// <summary>
/// 保存
/// </summary>
/// <param name="model">model</param>
/// <returns></returns>
ResultEx Save(<#=item.ModelName#> model);
/// <summary>
/// 删除
/// </summary>
/// <param name="ids">主键列表</param>
/// <returns></returns>
ResultEx Delete(List<int> ids);
#endregion
}
} 
<#}#>

```
以上是项目中使用的代码,保存之后生成IService代码

创建一个SvcTemplate.tt

```

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Windows.Forms" #>
<#@ Assembly Name="$(TargetDir)TemplateUtil.dll" #>

<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #> 
using System.Linq;
using System.Collections.Generic;
using EntityFramework.Extensions;
using Mehdime.Entity;
using Models;
using Models.DTOS;
using Services.Interfaces;
using Tools;
namespace Services.Template
{
<#List<TemplateUtil.TemplateName> ModelNames=TemplateUtil.GetModelNames();
foreach (var item in ModelNames)
{#>
public class <#=item.ModelName#>Svc : I<#=item.ModelName#>Svc
{
private readonly IDbContextScopeFactory _dbScopeFactory = new DbContextScopeFactory();

#region I<#=item.ModelName#>Svc

public <#=item.ModelName#> GetModel(int? id)
{
using (var dbScope = _dbScopeFactory.Create())
{
var db = dbScope.DbContexts.Get<ThirdIndustryAssociationDbContext>();
if (id == null)
return null;
else
return db.<#=item.EntityName#>.SingleOrDefault(p => p.Id == id.Value);
}
}

public object GetPager(GridPageModel pager, SearchModel search)
{
using (var dbScope = _dbScopeFactory.Create())
{
var db = dbScope.DbContexts.Get<ThirdIndustryAssociationDbContext>();
var query = from p in db.<#=item.EntityName#>
select p;
return new PagedList<<#=item.ModelName#>>(query, pager).ToPager();
}
}

public ResultEx Save(<#=item.ModelName#> model)
{
using (var dbScope = _dbScopeFactory.Create())
{
var db = dbScope.DbContexts.Get<ThirdIndustryAssociationDbContext>();
if (model.Id == 0)
{
db.<#=item.EntityName#>.Add(model);
}
else
{
db.Entry(model).State = System.Data.Entity.EntityState.Modified;
}
return ResultEx.Init(db.SaveChanges() > 0);
}
}


public ResultEx Delete(List<int> ids)
{
using (var dbScope = _dbScopeFactory.Create())
{
var db = dbScope.DbContexts.Get<ThirdIndustryAssociationDbContext>();
db.<#=item.EntityName#>.Where(p => ids.Contains(p.Id)).Delete();
return ResultEx.Init();
}
}
#endregion
}
<#}#>
}

```

以上的代码保存后生成Service代码

**TextTemplatingFilePreprocessor的使用创建一个控制台Demo,实现批量编写文件**
新建一个文本模板MultiArticle.tt,自定义工具改为TextTemplatingFilePreprocessor,然后保存修改,生成一个空的MultiArticle.cs

新建一个MultiArticleCode.cs类

```

public class Article
{
public string Title { get; set; }

public string Content { get; set; }
}

public partial class MultiArticle
{

public string Subject { get; set; }

public List<Article> ArticleList { get; set; }


public MultiArticle(string subject, List<Article> articles) { Subject = subject; ArticleList = articles; }

}

```

创建的MultiArticle类必须和tt模板生成的类在相同的命名空间下。


添加模板内容如下:


```

<#@ template language="C#" #>


<div style="text-align:center;">
<table width="600" cellpadding="0" cellspacing="0" border="0" style="margin:0 auto;"><tbody><tr><td>
<div style="600px;text-align:left;font:12px/15px simsun;color:#000;background:#fff;">
<!-- 水平居中的邮件 -->
<h1><#=Subject #></h2>
<table>
<# foreach(Article item in ArticleList)
{ #>
<tr><td> <#= item.Title #> </td><td> <#= item.Content #> </td></tr>
<# } #>
</table>

</div>
</td></tr></tbody></table>
</div>

```

在控制台的main方法中


```

class Program
{
static void Main(string[] args)
{
string subject = "最新消息";
var artList = new List<Article>();
for (int i = 0; i < 4; i++)
{
Article art = new Article();
art.Title = "标题" + i + ":" + Guid.NewGuid();
art.Content = "内容" + i + ":" + Guid.NewGuid();
artList.Add(art);
}

MultiArticle mod = new MultiArticle(subject, artList);
//TransformText方法是模板编译时自动产生的,
//这也是前面有提到的,模板MultiArticle.tt的命名要与MultiArticleCode.cs中的类名相同的原因,
//利用分部类partial,就可以调用该TransformText方法了。
String pageContent = mod.TransformText();
//将pageContent保存到任何你想保存的地方
System.IO.File.WriteAllText("outputPage.html", pageContent, Encoding.UTF8);
}
}

```

运行代码,就可以看到生成的文件了。