T4命名空间和类型解析

4

我正在尝试创建一个 T4 模板,它将获取我的应用程序全局资源,循环遍历静态属性并创建一个带有 const string 属性的静态类,这样我就可以通过使用强类型来获取资源名称作为 string

以下是当前的代码:

<#@ template debug="true" hostSpecific="true" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#  CodeGenerationTools code = new CodeGenerationTools(this);
    MetadataLoader loader = new MetadataLoader(this);
    CodeRegion region = new CodeRegion(this, 1);
    MetadataTools ef = new MetadataTools(this);
    string namespaceName = code.VsNamespaceSuggestion();
if (!String.IsNullOrEmpty(namespaceName))
{ #>
namespace <#=code.EscapeNamespace(namespaceName)#>
{
<#    PushIndent(CodeRegion.GetIndent(1));
} #>
    public static class ResourceStrings {
<#      var props = typeof(Resources).GetProperties(BindingFlags.Static);
        foreach (var prop in props) { #>
            public const string <#= prop.Name #> = "<#= prop.Name #>";
<#      } #>
    }
<# if (!String.IsNullOrEmpty(namespaceName))
{
    PopIndent(); #>
}
<# } #>
<#+
  // Insert any template procedures here
#>

问题在于它无法在 typeof() 中找到 Resources 命名空间。 Resources 命名空间是我的 Localize.designer.cs 文件(Localize.resx 的自动生成文件)的命名空间。
我错过了什么?
3个回答

5

我刚有了类似的想法。我想创建一个静态类,其中包含用户设置的const字符串值,以便在Windows表单绑定中使用强类型而不是字符串:

this.textBox1.DataBindings.Add("Text", Properties.Settings.Default, Properties.Settings.PropertyNames.SomeStringValue);

然而,我发现 t4 模板无法访问当前项目的类型。你需要使用以下代码加载文件:

<#@ assembly name="$(TargetPath)" #>

然而,如果你这样做并重新生成模板,在不关闭 Visual Studio 的情况下无法重建项目,因为该文件正在使用中。

简而言之,我放弃了这种方法,现在自己读取 app.config 文件以获取所需数据。修改一下就可以轻松地读取 Resources.xml 文件。

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>

<#
var xmlFile = Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "..", "app.config");
var query = from x in XElement.Load(xmlFile).Element("userSettings").Element("Your.Namespace.Properties.Settings").Elements("setting")
            select x;
#>

namespace Your.Namespace.Properties
{
    public sealed partial class Settings
    {
        public static class PropertyNames
        {

        <# foreach (var item in query) { #>
        public static readonly string <#=item.Attribute("name").Value#> = @"<#=item.Attribute("name").Value#>";
        <# } #>

        }
    }
}

这是一个很好的想法,谢谢。我实际上找到了ResxResourceReader类的参考,该类在System.Windows.Forms命名空间中,但在MVC应用程序内部遇到了一些问题,因此我切换到从RESX文件读取数据元素,并且它完美地工作了。 - mare
仅是一个快速的提示,对于那些能够使用 Visual Studio 2010 SP1 的人来说,文件被锁定使用的问题已经得到解决。 - GarethJ
@GarethJ:好知道,谢谢。但我也明白从T4模板内部访问同一程序集是很危险的(比如说我刚刚进行了清理并运行了我的模板。模板报告程序集丢失,所以我必须重新编译,但这不起作用,因为我的模板已经损坏并且我引用的生成代码也丢失了,我更喜欢这个解决方案 ;))。 - Jürgen Steinblock
同意 - 我也更喜欢代码方法,即使在紧密相关的程序集之间 - 当应用程序无法编译时,我讨厌代码生成出现故障。 - GarethJ

3

您可以使用以下方式获取当前命名空间的引用:

<#+
    // other template code here...
    static string CurrentNamespace = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint").ToString();

并将其用于模板输出,如下所示:

namespace <#=CurrentNamespace #>
{
    public static class MyGenerateClass
    {
       // other generated code here...

2

尝试使用T4Toolbox和Volatile Assembly指令。

<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"
Name="<Your assembly fully qualified name that you are concerned about locking but need a self reference to>"#>

它将创建程序集的影子副本,用于T4。

您需要下载并安装T4Toolbox才能使其工作。

http://t4toolbox.codeplex.com/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接