【问题标题】:Calling abstract and derived tt template in VS extension在 VS 扩展中调用抽象和派生的 tt 模板
【发布时间】:2018-09-17 14:16:24
【问题描述】:

我正在开发 VS 的扩展。它应该通过 TT 模板映射数据库对象。模板可以由扩展用户制作。唯一的事情是,必须继承抽象模板,这是扩展的一部分。我在 Microsoft 文档上找到了一个建议,以这种方式在 VS 扩展中调用转换:Invoking Text Transformation in a VS Extension 它工作正常,对于虚拟模板,如下所示:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>

<#
Generate(new List<string> {"AAA","BBB","CCC"},  "666");
#>
<#+ 
public void Generate(List<string> inputList, string tableName)
{
#>
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.Serialization;

namespace BBB 

public class <#= tableName #>
{
    <#+
    foreach(var t in inputList)
    {
    #>
        /// <summary>
        /// <#= t #>
        /// </summary>
    <#+
    }
    #>
}
<#+
}
#>

和代码:

Generate("..\\..\\Templates\\TextTemplate.tt");

public void Generate(string filePath)
    {
        IServiceProvider serviceProvider = ServiceProvider;
        ITextTemplating t4 = serviceProvider.GetService(typeof(STextTemplating)) as ITextTemplating;
        T4Callback cb = new T4Callback();
        string result = t4.ProcessTemplate(filePath, File.ReadAllText(filePath), cb);
        string resultFileName = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath)) + "_gen" + cb.fileExtension;
        File.WriteAllText(resultFileName, result, cb.outputEncoding);
    }


public class T4Callback : ITextTemplatingCallback
{
    public List<string> errorMessages = new List<string>();
    public string fileExtension = ".cs";
    public Encoding outputEncoding = Encoding.UTF8;

    public void ErrorCallback(bool warning, string message, int line, int column)
    { errorMessages.Add(message); }

    public void SetFileExtension(string extension)
    { fileExtension = extension; }

    public void SetOutputEncoding(Encoding encoding, bool fromOutputDirective)
    { outputEncoding = encoding; }
}

但它不适用于抽象模板。如果我有抽象模板:

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

<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#@ parameter type="System.String" name="FakeIn" #> 

<#   
this.PushIndent("  ");  

//Generate method
Generate(InputData); 

//Save method
SaveOutput(OutputFileName);

this.PopIndent();  
#>  

<#+ 
#region Properties
public string OutputFileName { get; set; }
public InPutClass InputData { get; set; }
#endregion

#region Override method
protected virtual void Generate(InPutClass input) { }
#endregion

#region Non-Override method
protected void SaveOutput(string outputFileName) 
{
    string outputFilePath = Path.Combine(outputFileName);
    File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString()); 
    this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
}
#endregion

#region Data classes
public class InPutClass
{
    public List<string> InputList { get; set; }
    public string InputTableName { get; set; }
}
#endregion
#>

并像这样派生出一个:

<#@ template language="C#" inherits="AbstractTemplate" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>

<#
InputData = new InPutClass{ InputList = new List<string>{"AAA","BBB","CCC"}, InputTableName = "abc" };
OutputFileName = "..\\..\\Templates\\file2.cs";
base.TransformText();  
#>

<#+ 
protected override void Generate(InPutClass input)
{
#>
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.Serialization;
using ABEL.CORE.TypeComponents;
using ABEL.DAL.Attributes;
using Protos.Data;

namespace BBB 

public class <#= input.InputTableName #>
{
    <#+
    foreach(var t in input.InputList)
    {
    #>
        /// <summary>
        /// <#= t #>
        /// </summary>
    <#+
    }
    #>
}
<#+
}
#>

如果我调用方法生成结果是:

错误生成输出

  • 编译转换:找不到类型或命名空间名称“AbstractTemplate”(是否缺少 using 指令或程序集引用?)
  • 编译转换:找不到类型或命名空间名称“InPutClass”(是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation.TransformText()”:找不到合适的方法来覆盖
  • 编译转换:'GeneratedTextTransformation.Generate(InPutClass)':找不到合适的方法来覆盖
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:当前上下文中不存在名称“InputData”
  • 编译转换:找不到类型或命名空间名称“InPutClass”(是否缺少 using 指令或程序集引用?)
  • 编译转换:名称“OutputFileName”在当前上下文中不存在
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“GenerationEnvironment”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“GenerationEnvironment”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“GenerationEnvironment”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“GenerationEnvironment”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)
  • 编译转换:“GeneratedTextTransformation”不包含“Write”的定义,并且找不到接受“GeneratedTextTransformation”类型的第一个参数的扩展方法“Write”(您是否缺少 using 指令或程序集引用?)

所以对我来说,模板对你自己一无所知。因为当我将它们都作为解决方案的一部分并只调用经典模板 template.TransformText();一切都好。

我也尝试了一些 PreprocessingTemplate 的组合,像这样:

   GenerateAbstract("\\..\\..\\Templates2\\DerivedTemplate2.tt");
   public void GenerateAbstract(string filePath)
    {
        IServiceProvider serviceProvider = ServiceProvider;
        ITextTemplating t4 = serviceProvider.GetService(typeof(STextTemplating)) as ITextTemplating;

        T4Callback cb = new T4Callback();
        string[] reference;
        string abstractTemplatePath = "..\\..\\Templates2\\AbstractTemplate.tt";

        string abstractTemplatePreprocessing = t4.PreprocessTemplate(abstractTemplatePath, File.ReadAllText(abstractTemplatePath), cb, "AbstractTemplate", "AbstractTemplating.Templates2", out reference);
        string derived2TemplatePreprocessing = t4.PreprocessTemplate(filePath, File.ReadAllText(filePath), cb, "DerivedTemplate2", "AbstractTemplating.Templates2", out reference);

        File.WriteAllText(Path.Combine("..\\..\\Templates2", "AbstractTemplate.cs"), abstractTemplatePreprocessing, cb.outputEncoding);
        File.WriteAllText(Path.Combine("..\\..\\Templates2", "DerivedTemplate2.cs"), derived2TemplatePreprocessing, cb.outputEncoding);

        string result = t4.ProcessTemplate("..\\..\\Templates2\\AbstractTemplate.cs", derived2TemplatePreprocessing, cb);

        string resultFileName = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath)) + "_gen" + cb.fileExtension;
        // Write the processed output to file:
        File.WriteAllText(resultFileName, result, cb.outputEncoding);
        // Append any error messages:
        if (cb.errorMessages.Count > 0)
        {
            File.AppendAllLines(resultFileName, cb.errorMessages);
        }
    }

但在这种情况下是 DerivedTemplate2 的结果 C# 代码:AbstractTemplate,类似于模板是解决方案的一部分并构建解决方案的情况。

所以最后我的问题可以以这种方式调用抽象和派生模板吗?如果是,请您举例说明如何实现这一目标。谢谢

【问题讨论】:

  • 您能解释一下您的扩展程序应该如何工作吗?它应该同时生成“抽象”和“衍生”模板?
  • 不,在派生模板中是必须由用户模板继承的方法 Generate()。所以输出应该是一个文件。文章中的扩展模板是抽象模板逻辑中的简化模板,它连接到数据库并获取数据以进行生成。并且在用户模板中应该定义如何处理数据库中的数据。

标签: c# t4 visual-studio-extensions


【解决方案1】:

这只是一个意见,但关于您所描述的主题:

  1. 不要对您的用户隐藏您的模板(将其与生成的代码一起放入解决方案中)-VS MVC-Entity-Extension 执行此操作的方法-相反-但在这种情况下 MS 是错误的(并且MS 世界中的代码生成就是这方面的证据)。如果您必须生成 100 个文件,请同时创建 100 个模板。或者在一个文件中生成一百个类的一个模板。

  2. 不要混合代码生成和实现继承——它们是相互正交的。首先是在表单用户“允许更改所有内容”中向用户发布“所有内容”,第二个是隐藏详细信息(以及如果您想为用户提供“您可以更改所有内容”功能,为什么要隐藏详细信息)?

【讨论】:

  • 感谢您的建议,但是:1) 模板没有隐藏,被复制到使用扩展名的解决方案中,连同模板一起选择用户。见下图:link json config 包含应生成的 db 对象列表并连接到数据库,配置由抽象模板读取并为用户模板准备数据,用户模板生成 *.cs 类
  • 2) 用户不能改变一切,他只能改变 db 中的数据在他的方法中的包装方式,而不是他获取数据的方式。这里的抽象模板的意思是,用户必须重写 Generate 方法并确保用户方法能够处理准备抽象模板的数据。如果用户使用了错误的模板,我预计会出现一些错误,他的模板不会继承抽象模板。
  • “用户不能改变一切”......伙计,这是开源世界......即使使用 MVC-Entity t4 模板(用于视图和控制器生成) - 也可以将模板包含到您的项目作为特定文件夹中的常规文件,使用 VS 配置引用它们,然后从向导中使用/重用它们(通过您的更改,例如它们生成 cshtml 的方式)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2016-01-04
  • 2013-12-27
  • 2021-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多