【问题标题】:Get referenced project's path in T4 template?在 T4 模板中获取引用项目的路径?
【发布时间】:2010-08-23 13:31:41
【问题描述】:

我有一个包含几个项目的解决方案。我想在我的一个测试项目中创建一些 T4 模板,以根据另一个项目中的代码生成测试。测试项目具有对另一个项目的项目引用。我遇到的问题是我不知道如何获取需要从中生成代码的 edmx 文件的文件路径。

示例(假设这是一个基于 ASCII 的解决方案资源管理器):

MySolution.sln
-> MyTests.csproj (C:\a\b\c\)
----> GeneratedTests.tt (C:\a\b\c\GeneratedTests.tt)
-> MyDAL.csproj (C:\x\y\z\)
----> MyModel.edmx (C:\x\y\z\MyModel.edmx)

我的 GeneratedTests.tt 如何能够利用其项目引用获取 MyModel.edmx 的文件路径?

【问题讨论】:

    标签: c# visual-studio-2010 t4


    【解决方案1】:

    此答案仅适用于 Visual Studio。

    设置 T4 模板的“主机特定”属性。这使您可以访问 Host 属性。将 Host 类型转换为 IServiceProvider 以调用 GetService(typeof(DTE))。这使您可以遍历解决方案的内容。

    <#@ template language="c#" hostspecific="true"  #>
    <#@ assembly name="EnvDTE" #>
    <#@ import namespace="EnvDTE" #>
    These are the projects in this solution:
    <#
    var serviceProvider = this.Host as IServiceProvider;
    var dte = serviceProvider.GetService(typeof(DTE)) as DTE;
    foreach (Project p in dte.Solution.Projects)
    {
    #>
        <#=p.Name#> at <#=p.FullName#>
    <#
    }
    #>
    

    另请参阅ITextTemplatingEngineHost interface on MSDNT4 Architecture by Oleg Synch 的示例。

    【讨论】:

    • 我认为这对我来说会很好,如果它听起来像它那样工作。但是,您能否详细说明您的评论,即这只适用于 Visual Studio?我们计划自动化我们的模板生成,因为我们开始将它们放在我们的解决方案中,因为手动完成并生成所有内容开始变得很痛苦。我们不确切知道我们将如何实现自动化,因此您的回答可以帮助我们规划它。 Nant、PS/MSBuild 等等 - 我们很好,但必须这样做。
    • 当您设置 hostspecific="true" 时,模板可以访问 Host 参数。但缺点是T4主机必须提供。在这种情况下,假定 T4 主机是 Visual Studio,它实现了 DTE 接口。命令行构建将返回 null。如果您想使用此方法,我建议您在 Visual Studio 中转换您的模板,而不是在构建中。自动构建可以使用生成的代码。
    【解决方案2】:

    根据 James Close 的评论,我能够编写以下模板来调试我的文件路径:

    <#@ template language="C#" debug="true" hostspecific="true"#>
    <#@ include file="EF.Utility.CS.ttinclude"#><#@
     output extension=".txt"#><#
    
    /////////Some standard-ish settings, continue reading on
    CodeGenerationTools code = new CodeGenerationTools(this);
    MetadataLoader loader = new MetadataLoader(this);
    CodeRegion region = new CodeRegion(this, 1);
    MetadataTools ef = new MetadataTools(this);
    
    /////////Below are the relevant sections I used for debugging
    
        string solutionsPath = Host.ResolveAssemblyReference("$(SolutionDir)");//Gives you the location of MySolution.sln
        string edmxFile = solutionsPath + "MyDAL/MyDAL/MyModel.edmx"; //Note - VS projects usually have a subdir with the same name as the sln, hence the repetition for MyDAL
    #>
    Does this file exist?
    
    <#
    //
    if (File.Exists(edmxFile))
    {
        //Continue.
        #>
        Yes
        <#
    }
    else
    {
        #>
        No
        <#
    }
    #>
    

    这将生成一个 .txt 文件,并且会非常快速地帮助您调试是否可以找到您的路径。

    附带说明,如果无法找到相对目录路径(例如../App.config),我发现它有助于在每个目录级别放置一个文件(例如test1.txt),正如我发现Host.ResolvePath 无法通过我的设置看到当前程序集之外的内容。由于../../App.config 可能会解析为MySolution\App.config,因此此警告可能会很快变得混乱,但../../MyDal/README.txt 不会解析(因此找不到文件),即使这是正确的路径。据我所知,上面的代码似乎否定了这个问题。

    上述解决方案也可能是该问题的解决方案 - How to use the poco entity generator

    【讨论】:

      【解决方案3】:

      使用这些行

      string path = this.Host.ResolvePath("");
      Directory.SetCurrentDirectory(path);
      

      然后使用相对路径获取您的 edmx 文件 例如,字符串 inputFile = @"..\Modal.edmx";

      【讨论】:

        【解决方案4】:

        这样不行。您必须通过路径引用 dll(您可以使用 Host.ResolvePath 找到它并使用工具箱中的 VolatileAssembly 标记以便能够在不重新启动 VS 的情况下重新编译它)并使用反射来处理模型。

        【讨论】:

        • 通过路径引用 DLL 会导致我试图避免的问题。一个开发盒可能将项目放在一个地方,而另一个开发盒将它放在另一个地方。如果我可以假设该位置,那么我就不必让模板确定它 - 我可以硬编码它或使用构建脚本注入它。处理模型没有问题 - 如果我对路径进行硬编码,我的脚本会完美运行。我只需要一种方法来动态确定该路径。如果我需要模板的路径,可以使用 Host.Resolve - 我只需要找出 MyDAL 程序集源的路径。
        【解决方案5】:

        根据 Mina 和其他人的回答,我想出了这个解决方案。它列出了当前工作目录、解决方案路径并使用 Mina 的技巧来更改活动工作目录。

        <#@ template debug="true" hostspecific="true" language="C#" #>
        <#@ output extension=".cs" #>
        <#@ import namespace="System.IO" #>
        <#@ assembly name="EnvDTE" #>
        <#@ import namespace="EnvDTE" #>
        <#
         string cwd1 = System.IO.Directory.GetCurrentDirectory();
         string solutionPath = Host.ResolveAssemblyReference("$(SolutionDir)");
         Directory.SetCurrentDirectory(solutionPath);
         string cwd2 = System.IO.Directory.GetCurrentDirectory();
        #>
        // Solutionpath is:<#= solutionPath #>, old cwd: <#= cwd1 #>, new cwd: <#= cwd2 #>
        

        【讨论】:

          【解决方案6】:

          您可以将宏用于特殊目录,例如模板中的$(ProjectDir)$(SolutionDir),也可以读取 .sln 或 .csproj 文件以提取其他项目的目录。

          【讨论】:

          • 我希望避免阅读 .sln 文件 - 我想主要是因为我不太了解它,而且我不确定我可以对路径的可靠性做出什么假设那里。你能给我一些更好地理解这一点的链接吗?
          • 其实我觉得不太合适。这些宏似乎只适用于 T4 指令,如 &lt;#@assembly #&gt;&lt;#@include #&gt;
          • 如果您需要解析 T4 指令之外的宏路径,您可以使用:strPath = Host.ResolveAssemblyReference("$(ProjectDir)"),ResolvePath 不适用于宏 - 也许未来的增强?
          猜你喜欢
          • 1970-01-01
          • 2012-04-05
          • 2019-01-28
          • 2013-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-12
          • 2014-06-13
          相关资源
          最近更新 更多