.NET Core 运行时T4模板使用,T4生成代码


.NET Core 运行时T4模板使用,T4生成代码

背景

利用T4模板自动生成代码

在.NET Framework中我们可以这样实现

添加引用:

Microsoft.VisualStudio.TextTemplating.10.0

Microsoft.VisualStudio.TextTemplating.Interfaces.10.0

VS2017以上,引用是

Microsoft.VisualStudio.TextTemplating.Interfaces.11.0

Microsoft.VisualStudio.TextTemplating.15.0

.NET Core 运行时T4模板使用,T4生成代码

利用T4模板生成字符串的代码

C# 全选
public static string CreateEntityClass(EntityModel classInfo)
{
	string templatePath = Common.T4Template_Entity;
	return CreateClass(classInfo, templatePath);
}


public static string CreateClass(object classInfo, string templatePath)
{
	if (!File.Exists(templatePath))
	{
		MessageBox.Show("未找到Entity.tt,请修改配置文件!\r\n" + templatePath);
		return null;
	}
	CustomTextTemplatingEngineHost host = new CustomTextTemplatingEngineHost();
	host.TemplateFileValue = templatePath;
	string input = File.ReadAllText(templatePath);
	host.Session = new TextTemplatingSession();

	host.Session.Add("entity", classInfo);

	string output = new Engine().ProcessTemplate(input, host);

	StringBuilder errorWarn = new StringBuilder();
	foreach (CompilerError error in host.Errors)
	{
		errorWarn.Append(error.Line).Append(":").AppendLine(error.ErrorText);
	}
	if (!File.Exists("Error.log"))
	{
		using ( var f=File.Create("Error.log"))
		{
		}
		
	}
	File.WriteAllText("Error.log", errorWarn.ToString());
	return output;
}

CustomTextTemplatingEngineHost.cs

C# 全选
public class CustomTextTemplatingEngineHost : ITextTemplatingEngineHost, ITextTemplatingSessionHost
{
	#region ITextTemplatingEngineHost
	internal string TemplateFileValue;
	public string TemplateFile
	{
		get { return TemplateFileValue; }
	}
   
	private string fileExtensionValue = ".txt";
	public string FileExtension
	{
		get { return fileExtensionValue; }
	}
	
	private Encoding fileEncodingValue = Encoding.UTF8;
	public Encoding FileEncoding
	{
		get { return fileEncodingValue; }
	}
	private CompilerErrorCollection errorsValue;
	public CompilerErrorCollection Errors
	{
		get { return errorsValue; }
	}
	public IList<string> StandardAssemblyReferences
	{
		get
		{
			return new string[]
			{
				typeof(System.Uri).Assembly.Location
			};
		}
	}
	public IList<string> StandardImports
	{
		get
		{
			return new string[]
			{
				"System"
			};
		}
	}
	public bool LoadIncludeText(string requestFileName, out string content, out string location)
	{
		content = System.String.Empty;
		location = System.String.Empty;

		if (File.Exists(requestFileName))
		{
			content = File.ReadAllText(requestFileName);
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public object GetHostOption(string optionName)
	{
		object returnObject;
		switch (optionName)
		{
			case "CacheAssemblies":
				returnObject = true;
				break;
			default:
				returnObject = null;
				break;
		}
		return returnObject;
	}
   
	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)
	{
		if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
		{
			//return typeof();
		}
		throw new Exception("Directive Processor not found");
	}
   
	public string ResolvePath(string fileName)
	{
		if (fileName == null)
		{
			throw new ArgumentNullException("the file name cannot be null");
		}
		if (File.Exists(fileName))
		{
			return fileName;
		}
		string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
		if (File.Exists(candidate))
		{
			return candidate;
		}
		return fileName;
	}

	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");
		}
		return String.Empty;
	}

	public void SetFileExtension(string extension)
	{
		fileExtensionValue = extension;
	}
	
	public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
	{
		fileEncodingValue = encoding;
	}
	
	public void LogErrors(CompilerErrorCollection errors)
	{
		errorsValue = errors;
	}
   
	public AppDomain ProvideTemplatingAppDomain(string content)
	{
		return AppDomain.CreateDomain("Generation App Domain");
	}

	#endregion

	#region ITextTemplatingSessionHost
	public ITextTemplatingSession CreateSession()
	{
		return Session;
	}

	public ITextTemplatingSession Session
	{
		get;
		set;
	}
	#endregion
}

在.NET Core中使用

.NET Core中无法引用 Microsoft.VisualStudio.TextTemplating.15.0 (不支持.NETCore版本)

.NET Core 运行时T4模板使用,T4生成代码

参考:

https://github.com/dotnet/runtime/issues/45048#issuecomment-731563466

在.NET Core中T4模板的使用可以用 SubSonic.Core.TextTemplating

使用方法

项目中添加 SubSonic.Core.TextTemplation 的Nuget引用

.NET Core 运行时T4模板使用,T4生成代码

.NET Core 运行时T4模板使用,T4生成代码

测试代码:

C# 全选
using Microsoft.AspNetCore.Mvc;
using Mono.TextTemplating;
using Mono.VisualStudio.TextTemplating;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace YESCMS.Controllers
{
    public class testController : ControllerBase
    {
        public IActionResult T4()
        {

            string input =
                "<#@ template language=\"C#\" #>\r\n" +
                "<#  foreach(string str in new string[]{\"a\",\"b\",\"c\",\"d\",\"e\"})" +
                "{ #>" +
                "    [<#= str#>]" +
                "<#" +
                "} #>";



            var gen = new TemplateGenerator();
            string tmp = null;
            string tmp2 = null;
            bool b = gen.ProcessTemplate(null, input, ref tmp, out tmp2);
            return Content(tmp2);
        }
        string Preprocess(string input, DummyHost host)
        {
            string className = "PreprocessedTemplate";
            string classNamespace = "Templating";
            string language = null;
            string[] references = null;


            TemplatingEngine engine = new TemplatingEngine();
            string output = engine.PreprocessTemplate(input, host, className, classNamespace, out language, out references);
            ReportErrors(host.Errors.ToCompilerErrorCollection());
            if (output != null)
            {
                return TemplatingEngineHelper.CleanCodeDom(output, "\n");
            }
            return null;
        }

        void ReportErrors(CompilerErrorCollection errors)
        {
            foreach (CompilerError error in errors)
            {
                Console.WriteLine(error.ErrorText);
            }
        }


    }

    public static class TemplatingEngineHelper
    {
        /// <summary>
        /// Cleans CodeDOM generated code so that Windows/Mac and Mono/.NET output can be compared.
        /// </summary>
        public static string CleanCodeDom(string input, string newLine)
        {
            using (var writer = new StringWriter())
            {
                using (var reader = new StringReader(input))
                {

                    bool afterLineDirective = true;
                    bool stripHeader = true;

                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {

                        if (stripHeader)
                        {
                            if (line.StartsWith("//", StringComparison.Ordinal) || StringUtil.IsNullOrWhiteSpace(line))
                                continue;
                            stripHeader = false;
                        }

                        if (afterLineDirective)
                        {
                            if (StringUtil.IsNullOrWhiteSpace(line))
                                continue;
                            afterLineDirective = false;
                        }

                        if (line.Contains("#line"))
                        {
                            afterLineDirective = true;
                        }

                        writer.Write(line);
                        writer.Write(newLine);
                    }
                }
                return writer.ToString();
            }
        }
    }

    internal static class StringUtil
    {
        public static bool IsNullOrWhiteSpace(String value)
        {
            if (value == null) return true;

            for (int i = 0; i < value.Length; i++)
            {
                if (!char.IsWhiteSpace(value[i])) return false;
            }

            return true;
        }

        public static TType GetValue<TType>(this PropertyInfo property, object @object, object[] index)
        {
            if (property.GetValue(@object, index) is TType success)
            {
                return success;
            }
            return default;
        }


        public static bool TryParse<TEnum>(this string value, out TEnum @enum)
            where TEnum : struct
        {
#if NET35
			if (Enum.Parse (typeof (TEnum), value) is TEnum success) {
				@enum = success;

				return true;
			}

			@enum = default;

			return false;
#else
            return Enum.TryParse(value, out @enum);
#endif
        }

    }

    public class DummyHost : ITextTemplatingEngineHost
    {
        public readonly Dictionary<string, string> Locations = new Dictionary<string, string>();
        public readonly Dictionary<string, string> Contents = new Dictionary<string, string>();
        public readonly Dictionary<string, object> HostOptions = new Dictionary<string, object>();
        List<string> standardAssemblyReferences = new List<string>(new[] { typeof(TemplatingEngine).Assembly.Location });
        List<string> standardImports = new List<string>();
        public readonly TemplateErrorCollection Errors = new TemplateErrorCollection();
        public readonly Dictionary<string, Type> DirectiveProcessors = new Dictionary<string, Type>();
        public readonly Dictionary<string, string> Parameters = new Dictionary<string, string>();

        public virtual object GetHostOption(string optionName)
        {
            object o;
            HostOptions.TryGetValue(optionName, out o);
            return o;
        }

        public virtual bool LoadIncludeText(string requestFileName, out string content, out string location)
        {
            content = null;
            return Locations.TryGetValue(requestFileName, out location)
                && Contents.TryGetValue(requestFileName, out content);
        }

        public virtual void LogErrors(TemplateErrorCollection errors)
        {
            Errors.AddRange(errors);
        }


        public virtual AppDomain ProvideTemplatingAppDomain(string content)
        {
            return null;
        }

        public virtual string ResolveAssemblyReference(string assemblyReference)
        {
            throw new System.NotImplementedException();
        }

        public virtual Type ResolveDirectiveProcessor(string processorName)
        {
            Type t;
            DirectiveProcessors.TryGetValue(processorName, out t);
            return t;
        }

        public virtual string ResolveParameterValue(string directiveId, string processorName, string parameterName)
        {
            return Parameters[parameterName];
        }

        public virtual string ResolvePath(string path)
        {
            throw new System.NotImplementedException();
        }

        string extension;

        public virtual void SetFileExtension(string extension)
        {
            this.extension = extension;
        }

        public virtual void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
        {

        }



        public virtual IList<string> StandardAssemblyReferences
        {
            get { return standardAssemblyReferences; }
        }

        public virtual IList<string> StandardImports
        {
            get { return standardImports; }
        }

        public virtual string TemplateFile
        {
            get; set;
        }
    }
}

运行测试:

.NET Core 运行时T4模板使用,T4生成代码

 

参考

SubSonic.Core.TextTemplating GitHub地址:https://github.com/SubSonic-Core/SubSonic.Core.TextTemplating

 

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:.NET Core 运行时T4模板使用,T4生成代码
下一篇:CodeMirror Razor支持
评论列表

发表评论

评论内容
昵称:
关联文章

YES-CMS内容管理系统 售价

联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码