MVC中使用T4模板

参考博文

http://www.cnblogs.com/heyuquan/archive/2012/07/26/2610959.html

图片释义

1、简单示例,对基本的模块标记

MVC中使用T4模板

2、根据上图生成的类

MVC中使用T4模板

网上大佬写的JuCheap项目中的T4模板

https://gitee.com/jucheap/projects

T4代码

<#@ template  language="C#"    debug="true" hostspecific="True"#>
<#@ include file="$(SolutionDir)JuCheap.Service\MultipleOutputHelper.ttinclude" #>
<#@ import namespace="System"#> <#
string solutionsPath = Host.ResolveAssemblyReference("$(SolutionDir)");
var files = System.IO.Directory.GetFiles(solutionsPath + @"\JuCheap.Entity", "*.cs"); var manager = Manager.Create(Host, GenerationEnvironment); //1.开始输出接口契约文件
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
var name = file.Name.Replace("Entity.cs",string.Empty);
var lowerName =name.ToLower();
//定义输出文件
manager.StartNewFile("I"+name+"Service.Partial.cs", string.Empty);
#> /*******************************************************************************
* Copyright (C) JuCheap.Com
*
* Author: dj.wong
* Create Date: <#=DateTime.Now#>
* Description: Automated building by service@JuCheap.com
*
* Revision History:
* Date Author Description
* 2017-08-02 dj.wong optimization
*********************************************************************************/ using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using JuCheap.Service.Dto; namespace JuCheap.Service.Abstracts
{
/// <summary>
/// <#=name#>业务契约
/// </summary>
public partial interface I<#=name#>Service
{
/// <summary>
/// 添加<#=lowerName#>
/// </summary>
/// <param name="<#=lowerName#>"><#=lowerName#>实体</param>
/// <returns></returns>
bool Add(<#=name#>Dto <#=lowerName#>); /// <summary>
/// 批量添加<#=lowerName#>
/// </summary>
/// <param name="models"><#=lowerName#>集合</param>
/// <returns></returns>
bool Add(List<<#=name#>Dto> models); /// <summary>
/// 编辑<#=lowerName#>
/// </summary>
/// <param name="<#=lowerName#>">实体</param>
/// <returns></returns>
bool Update(<#=name#>Dto <#=lowerName#>); /// <summary>
/// 批量更新<#=lowerName#>
/// </summary>
/// <param name="<#=lowerName#>s"><#=lowerName#>实体集合</param>
/// <returns></returns>
bool Update(IEnumerable<<#=name#>Dto> <#=lowerName#>s); /// <summary>
/// 删除<#=lowerName#>
/// </summary>
/// <param name="id">Id</param>
/// <returns></returns>
bool Delete(int id); /// <summary>
/// 批量删除<#=lowerName#>
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
bool Delete(Expression<Func<<#=name#>Dto, bool>> exp); /// <summary>
/// 获取单条符合条件的 <#=lowerName#> 数据
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
<#=name#>Dto GetOne(Expression<Func<<#=name#>Dto, bool>> exp); /// <summary>
/// 查询符合调价的 <#=lowerName#>
/// </summary>
/// <param name="exp">过滤条件</param>
/// <param name="orderExp">排序条件</param>
/// <param name="isDesc">是否是降序排列</param>
/// <returns></returns>
List<<#=name#>Dto> Query<OrderKeyType>(Expression<Func<<#=name#>Dto, bool>> exp, Expression<Func<<#=name#>Dto, OrderKeyType>> orderExp, bool isDesc = true); /// <summary>
/// 分页获取<#=lowerName#>
/// </summary>
/// <param name="queryBase">QueryBase</param>
/// <param name="exp">过滤条件</param>
/// <param name="orderExp">排序条件</param>
/// <param name="isDesc">是否是降序排列</param>
/// <returns></returns>
ResultDto<<#=name#>Dto> GetWithPages<OrderKeyType>(QueryBase queryBase, Expression<Func<<#=name#>Dto, bool>> exp, Expression<Func<<#=name#>Dto, OrderKeyType>> orderExp, bool isDesc = true); /// <summary>
/// 分页获取<#=lowerName#>
/// </summary>
/// <param name="queryBase">QueryBase</param>
/// <param name="exp">过滤条件</param>
/// <param name="orderBy">排序条件</param>
/// <param name="orderDir">是否是降序排列</param>
/// <returns></returns>
ResultDto<<#=name#>Dto> GetWithPages(QueryBase queryBase, Expression<Func<<#=name#>Dto, bool>> exp, string orderBy, string orderDir = "desc");
}
}
<#
// 结束输出文件
manager.EndBlock();
}
//2.开始生成契约实现文件
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
var name = file.Name.Replace("Entity.cs",string.Empty);
var lowerName = name.ToLower();
//定义输出文件
manager.StartNewFile(name+"Service.Partial.cs", string.Empty);
#> /*******************************************************************************
* Copyright (C) JuCheap.Com
*
* Author: dj.wong
* Create Date: <#=DateTime.Now#>
* Description: Automated building by service@JuCheap.com
*
* Revision History:
* Date Author Description
*
*********************************************************************************/ using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Linq.Expressions;
using EntityFramework.Extensions;
using AutoMapper;
using JuCheap.Core;
using JuCheap.Core.Extentions;
using JuCheap.Entity;
using JuCheap.Service.Dto;
using Mehdime.Entity; namespace JuCheap.Service.Abstracts
{
/// <summary>
/// <#=name#>业务契约
/// </summary>
public partial class <#=name#>Service : ServiceBase<<#=name#>Entity>, IDependency, I<#=name#>Service
{
#region 构造函数注册上下文
public IDbContextScopeFactory _dbScopeFactory {get;set;} //private readonly IDbContextScopeFactory _dbScopeFactory; //public <#=name#>Service(IDbContextScopeFactory dbScopeFactory)
//{
// _dbScopeFactory = dbScopeFactory;
//} #endregion #region I<#=name#>Service 接口实现 /// <summary>
/// 添加<#=lowerName#>
/// </summary>
/// <param name="dto"><#=lowerName#>实体</param>
/// <returns></returns>
public bool Add(<#=name#>Dto dto)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var entity = Mapper.Map<<#=name#>Dto, <#=name#>Entity>(dto);
dbSet.Add(entity);
var count = db.SaveChanges();
return count > 0;
}
} /// <summary>
/// 批量添加<#=lowerName#>
/// </summary>
/// <param name="dtos"><#=lowerName#>集合</param>
/// <returns></returns>
public bool Add(List<<#=name#>Dto> dtos)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var entities = Mapper.Map<List<<#=name#>Dto>, List<<#=name#>Entity>>(dtos);
dbSet.AddRange(entities);
return db.SaveChanges() > 0;
}
} /// <summary>
/// 编辑<#=lowerName#>
/// </summary>
/// <param name="dto">实体</param>
/// <returns></returns>
public bool Update(<#=name#>Dto dto)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var entity = Mapper.Map<<#=name#>Dto, <#=name#>Entity>(dto);
dbSet.AddOrUpdate(entity);
return db.SaveChanges() > 0;
}
} /// <summary>
/// 批量更新<#=lowerName#>
/// </summary>
/// <param name="dtos"><#=lowerName#>实体集合</param>
/// <returns></returns>
public bool Update(IEnumerable<<#=name#>Dto> dtos)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var entities = Mapper.Map<IEnumerable<<#=name#>Dto>, IEnumerable<<#=name#>Entity>>(dtos);
dbSet.AddOrUpdate(entities.ToArray());
return db.SaveChanges() > 0;
}
} /// <summary>
/// 删除<#=lowerName#>
/// </summary>
/// <param name="id">Id</param>
/// <returns></returns>
public bool Delete(int id)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db); var model = dbSet.FirstOrDefault(item => item.Id == id);
dbSet.Remove(model);
return db.SaveChanges() > 0;
}
} /// <summary>
/// 批量删除<#=lowerName#>
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
public bool Delete(Expression<Func<<#=name#>Dto, bool>> exp)
{
using (var scope = _dbScopeFactory.Create())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var where = exp.Cast<<#=name#>Dto, <#=name#>Entity, bool>(); var models = dbSet.Where(where);
dbSet.RemoveRange(models);
return db.SaveChanges() > 0;
}
} /// <summary>
/// 获取单条符合条件的 <#=lowerName#> 数据
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
public <#=name#>Dto GetOne(Expression<Func<<#=name#>Dto, bool>> exp)
{
using (var scope = _dbScopeFactory.CreateReadOnly())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var where = exp.Cast<<#=name#>Dto, <#=name#>Entity, bool>();
var entity = dbSet.AsNoTracking().FirstOrDefault(where); return Mapper.Map<<#=name#>Entity, <#=name#>Dto>(entity);
}
} /// <summary>
/// 查询符合调价的 <#=lowerName#>
/// </summary>
/// <param name="exp">过滤条件</param>
/// <param name="orderExp">排序条件</param>
/// <param name="isDesc">是否是降序排列</param>
/// <returns></returns>
public List<<#=name#>Dto> Query<OrderKeyType>(Expression<Func<<#=name#>Dto, bool>> exp, Expression<Func<<#=name#>Dto, OrderKeyType>> orderExp, bool isDesc = true)
{
using (var scope = _dbScopeFactory.CreateReadOnly())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var where = exp.Cast<<#=name#>Dto, <#=name#>Entity, bool>();
var order = orderExp.Cast<<#=name#>Dto, <#=name#>Entity, OrderKeyType>();
var query = GetQuery(dbSet, where, order, isDesc);
var list = query.ToList();
return Mapper.Map<List<<#=name#>Entity>, List<<#=name#>Dto>>(list);
}
} /// <summary>
/// 分页获取<#=name#>
/// </summary>
/// <param name="queryBase">QueryBase</param>
/// <param name="exp">过滤条件</param>
/// <param name="orderExp">排序条件</param>
/// <param name="isDesc">是否是降序排列</param>
/// <returns></returns>
public ResultDto<<#=name#>Dto> GetWithPages<OrderKeyType>(QueryBase queryBase, Expression<Func<<#=name#>Dto, bool>> exp, Expression<Func<<#=name#>Dto, OrderKeyType>> orderExp, bool isDesc = true)
{
using (var scope = _dbScopeFactory.CreateReadOnly())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var where = exp.Cast<<#=name#>Dto, <#=name#>Entity, bool>();
var order = orderExp.Cast<<#=name#>Dto, <#=name#>Entity, OrderKeyType>();
var query = GetQuery(dbSet, where, order, isDesc); var query_count = query.FutureCount();
var query_list = query.Skip(queryBase.Start).Take(queryBase.Length).Future();
var list = query_list.ToList(); var dto = new ResultDto<<#=name#>Dto>
{
recordsTotal = query_count.Value,
data = Mapper.Map<List<<#=name#>Entity>, List<<#=name#>Dto>>(list)
};
return dto;
}
} /// <summary>
/// 分页获取<#=name#>
/// </summary>
/// <param name="queryBase">QueryBase</param>
/// <param name="exp">过滤条件</param>
/// <param name="orderBy">排序条件</param>
/// <param name="orderDir">排序类型:desc(默认)/asc</param>
/// <returns></returns>
public ResultDto<<#=name#>Dto> GetWithPages(QueryBase queryBase, Expression<Func<<#=name#>Dto, bool>> exp, string orderBy, string orderDir = "desc")
{
using (var scope = _dbScopeFactory.CreateReadOnly())
{
var db = GetDb(scope);
var dbSet = GetDbSet(db);
var where = exp.Cast<<#=name#>Dto, <#=name#>Entity, bool>();
//var order = orderExp.Cast<<#=name#>Dto, <#=name#>Entity, OrderKeyType>();
var query = GetQuery(dbSet, where, orderBy, orderDir); var query_count = query.FutureCount();
var query_list = query.Skip(queryBase.Start).Take(queryBase.Length).Future();
var list = query_list.ToList(); var dto = new ResultDto<<#=name#>Dto>
{
recordsTotal = query_count.Value,
data = Mapper.Map<List<<#=name#>Entity>, List<<#=name#>Dto>>(list)
};
return dto;
}
} #endregion
}
}
<#
// 结束输出文件
manager.EndBlock();
}
#> <#
manager.StartNewFile("AutoMapperConfiguration.Partial.cs", string.Empty);
#> /*******************************************************************************
* Copyright (C) JuCheap.Com
*
* Author: dj.wong
* Create Date: 2015/8/7 11:12:12
* Description: Automated building by service@JuCheap.com
*
* Revision History:
* Date Author Description
*
*********************************************************************************/ using AutoMapper;
using JuCheap.Entity;
using JuCheap.Service.Dto; namespace JuCheap.Service
{
/// <summary>
/// AutoMapper 配置
/// </summary>
public partial class AutoMapperConfiguration
{
/// <summary>
/// 配置AutoMapper
/// </summary>
public static void Config()
{
<#
//1.开始输出接口契约文件
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
var name = file.Name.Replace("Entity.cs",string.Empty);
var lowerName =name.ToLower();
//定义输出文件
#>
Mapper.CreateMap<<#=name#>Entity, <#=name#>Dto>();
Mapper.CreateMap<<#=name#>Dto, <#=name#>Entity>();
<#
}
#>
}
}
}
<#
// 结束输出文件
manager.EndBlock();
// 执行编译
manager.Process(true);
#>

引用的ttInclude代码

<#@ assembly name="System.Core"
#><#@ assembly name="System.Data.Linq"
#><#@ assembly name="EnvDTE"
#><#@ assembly name="System.Xml"
#><#@ assembly name="System.Xml.Linq"
#><#@ import namespace="System.Collections.Generic"
#><#@ import namespace="System.IO"
#><#@ import namespace="System.Text"
#><#@ import namespace="Microsoft.VisualStudio.TextTemplating"
#><#+
// https://raw.github.com/damieng/DamienGKit
// http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
// Manager class records the various blocks so it can split them up
class Manager {
private class Block {
public String Name;
public string FilePath;
public int Start, Length;
public bool IncludeInDefault;
} private Block currentBlock;
private readonly List<Block> files = new List<Block>();
private readonly Block footer = new Block();
private readonly Block header = new Block();
private readonly ITextTemplatingEngineHost host;
private readonly StringBuilder template;
protected readonly 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, string filePath) {
if (name == null)
throw new ArgumentNullException("name");
CurrentBlock = new Block { Name = name, FilePath = filePath };
} public void StartFooter(bool includeInDefault = true) {
CurrentBlock = footer;
footer.IncludeInDefault = includeInDefault;
} public void StartHeader(bool includeInDefault = true) {
CurrentBlock = header;
header.IncludeInDefault = includeInDefault;
} 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, bool sync = true) {
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();
if (!footer.IncludeInDefault)
template.Remove(footer.Start, footer.Length);
foreach(Block block in files) {
String myPath = block.FilePath;
if(!string.IsNullOrWhiteSpace(myPath))
outputPath = myPath;
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);
}
if (!header.IncludeInDefault)
template.Remove(header.Start, header.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 readonly EnvDTE.ProjectItem templateProjectItem;
private readonly EnvDTE.DTE dte;
private readonly Action<String> checkOutAction;
private readonly Action<List<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, bool sync) {
if (templateProjectItem.ProjectItems == null)
return;
base.Process(split, sync);
if (sync)
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 = fileName => dte.SourceControl.CheckOutItem(fileName);
projectSyncAction = keepFileNames => ProjectSync(templateProjectItem, keepFileNames);
} private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, List<String> keepFileNames) {
var keepFileNameSet = new HashSet<String>(keepFileNames);
var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.FileNames[0]) + ".";
foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
projectFiles.Add(projectItem.FileNames[0], 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));
}
}
} #>

用到的语法:

1、string solutionsPath = Host.ResolveAssemblyReference("$(SolutionDir)");

T4模板中获取当前解决方案的文件路径。

2、var files = System.IO.Directory.GetFiles(solutionsPath + @"\JuCheap.Entity", "*.cs");

获取JuCheap.Entity项目中所有cs扩展名的文件信息。

3、var manager = Manager.Create(Host, GenerationEnvironment);

在引用的ttInclude中,创建一个模板管理者的对象,用于操作和生成数据。

4、 public partial interface I<#=name#>Service

在<#  string name="ClassName" #> 中声明的变量,

可以在文本块中以<#=name#>的形式调用。(name为声明的变量名称)

5、 manager.EndBlock();

在引用的ttInclude中,结束对新服务类的内容赋值,

将文件对象添加到文件集合中。

6、manager.Process(true);

在引用的ttInclude中,结束数据生成,编译运行,生成相应的文件。

总结

因为有CodeSmith的使用经验,所以在看到T4模板使用方法时,

没有想象的那么困难。

纸上得来终觉浅,绝知此事要躬行。

目前只是看网上大佬的教程,还没有在项目中实际使用过,

还有许多未知的问题和操作。

上一篇:VS2013中的MVC5模板部署到mono上的艰辛历程


下一篇:Linux 配置