MVC中使用T4模板

参考博文

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

图片释义

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

2、根据上图生成的类

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

https://gitee.com/jucheap/projects

T4代码

<#@ template  language="C#"    debug="true" hostspecific="True"#> 
<#@ include file="$(SolutionDir)JuCheap.ServiceMultipleOutputHelper.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模板使用方法时,

没有想象的那么困难。

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

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

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

原文地址:https://www.cnblogs.com/masonblog/p/9233178.html