14

例如,EntityFramework Microsoft.EntityFrameworkCore.Relational 项目在资源文件中有以下文本:

...
<data name="FromSqlMissingColumn" xml:space="preserve">
  <value>The required column '{column}' was not present in the results of a 'FromSql' operation.</value>
</data>
...

生成以下 C# 代码:

...
/// <summary>
/// The required column '{column}' was not present in the results of a 'FromSql' operation.
/// </summary>
public static string FromSqlMissingColumn([CanBeNull] object column)
{
    return string.Format(CultureInfo.CurrentCulture, GetString("FromSqlMissingColumn", "column"), column);
}
...
private static string GetString(string name, params string[] formatterNames)
{
    var value = _resourceManager.GetString(name);

    Debug.Assert(value != null);

    if (formatterNames != null)
    {
        for (var i = 0; i < formatterNames.Length; i++)
        {
            value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
        }
    }

    return value;
}
...

但是当我在 VS 中编辑文件并保存它时,我只会生成简单的属性,例如:

...
/// <summary>
/// The required column '{column}' was not present in the results of a 'FromSql' operation.
/// </summary>
public static string FromSqlMissingColumn
{
    get { return ResourceManager.GetString("FromSqlMissingColumn"); }
}
...

有问题的文件可以在这里找到:

那么问题又来了——他们是怎么做到的,我怎么能得到同样的结果?

4

2 回答 2

11

他们是如何做到的呢?

首先应该很明显,他们不使用标准ResXFileCodeGenerator,而是使用一些自定义代码生成工具。

Custom Tool目前有 2 种标准的代码生成方式 - 使用类似于的老式方式ResXFileCodeGenerator,或使用T4 模板的现代方式。所以让我们看看。

Microsoft.EntityFrameworkCore.Relational.csproj文件中的对应条目如下所示:

<ItemGroup> 
    <EmbeddedResource Include="Properties\RelationalStrings.resx">
        <LogicalName>Microsoft.EntityFrameworkCore.Relational.Properties.RelationalStrings.resources</LogicalName> 
    </EmbeddedResource> 
</ItemGroup> 

正如我们所看到的,他们绝对不使用Custom Tool.

所以它应该是一个T4模板。事实上,在上述项目之后,我们可以看到:

<ItemGroup> 
    <Content Include="..\..\tools\Resources.tt"> 
        <Link>Properties\Resources.tt</Link> 
            <Generator>TextTemplatingFileGenerator</Generator> 
            <LastGenOutput>Resources.cs</LastGenOutput> 
            <CustomToolNamespace>Microsoft.EntityFrameworkCore.Internal</CustomToolNamespace> 
    </Content> 
    <Content Include="Properties\Microsoft.EntityFrameworkCore.Relational.rd.xml" /> 
</ItemGroup> 

所以你去吧!

xml现在,如果不深入实现,我不知道包含文件的目的是什么(它可能是生成器使用的东西,如选项或其他东西),但实际代码生成包含在以下Resources.tt文件。

我怎么能得到同样的结果?

我猜你是在要求你自己的项目。好吧,你可以做类似的事情。选择您的resx文件,转到Properties并清除Custom Tool. 然后添加T4 template到您的项目并编写代码生成(我不确定许可证是否允许您使用他们的代码,所以如果您想这样做,请确保您首先检查是否允许)。但原理是一样的。

于 2016-08-24T15:57:42.030 回答
4

我认为,EF 团队Custom Tool为此目的使用了自己的自定义。但是 Visual StudioPublicResXFileCodeGenerator用作.resx文件的默认自定义工具,并且该工具没有这样的功能,PublicResXFileCodeGenerator并且它的基类ResXFileCodeGenerator(都可以在Microsoft.VisualStudio.Design程序集中找到)只是 Visual Studio 的包装器StronglyTypedResourceBuilder

它们是工具IVsSingleFileGenerator(位于Microsoft.VisualStudio.Shell.Interop装配体中)。所以这是您可以开始实施自己的Custom Tool. 开始新Class Library的,添加Microsoft.VisualStudio.Shell.14.0Microsoft.VisualStudio.Shell.Interop引用。创建新类并实现此接口。界面IVsSingleFileGenerator非常简单。它只包含两种方法:

  • DefaultExtension它返回生成文件的扩展名(带有前导句点)作为out string pbstrDefaultExtension参数和VSConstant.S_OK返回值(当然,如果一切正常)。

  • Generate这是接受:

    • wszInputFilePath- 输入文件路径,可能为空,请勿使用。
    • bstrInputFileContents- 输入文件内容,应该使用。
    • wszDefaultNamespace- 默认命名空间(现在无法说明为什么ResXFileCodeGenerator与 Visual Studio 互操作以获取命名空间而不是使用此参数)。
    • rgbOutputFileContents- 生成文件的字节数组。您必须在返回的字节数组中包含 UNICODE 或 UTF-8 签名字节,因为这是一个原始流。rgbOutputFileContents 的内存必须使用 .NET Framework 调用Marshal.AllocCoTaskMem或等效的 Win32 系统调用来分配CoTaskMemAlloc。项目系统负责释放此内存。
    • pcbOutputrgbOutputFileContent-数组中的字节数。
    • pGenerateProgress- 对接口的引用,IVsGeneratorProgress生成器可以通过该接口向项目系统报告其进度。

    VSConstant.S_OK如果一切正常,或相应的错误代码,则返回。

还有关于实施的小指南。但是本指南并没有说明太多。最有用的是如何注册自己的生成器。

您最好深入研究ResXFileCodeGenerator代码(或简单地反编译)以获取实现示例或获取一些提示,例如如何与 Visual Studio 互操作。但我认为没有理由与 VS 互操作,因为您已经提供了您需要的一切。.resx文件内容可以被ResXResourceReader.FromFileContents.

剩下的很简单,因为您有资源名称和值,并且只需要返回生成文件的字节数组。我认为,将资源值解析为具有无效格式的编译时错误(例如:){{param}}}将是最大的困难。

当为即将到来的方法解析的值和参数找到时,您可以生成代码(再次作为示例,您可以参考ResXFileCodeGeneratorand StronglyTypedResourceBuilder,或者通过 CodeDom 以您想要的方式自行完成或手动编写源代码文本)。这也应该不难,因为您已经有一个您需要在发布的问题中生成的方法的示例。

编译您自己的生成器,注册它,将其设置在文件的Custom Tool属性中.resx,您将获得带有方法而不是属性的资源类。

你也可以在 github 上与其他人分享。:)


这是自定义工具注册的说明(因为 msdn 链接可能迟早会死掉):

若要使自定义工具在 Visual Studio 中可用,您必须注册它,以便 Visual Studio 可以实例化它并将其与特定项目类型相关联。

  1. 在 Visual Studio 本地注册表或系统注册表中的HKEY_CLASSES_ROOT.

    例如,以下MSDataSetGenerator是 Visual Studio 附带的托管自定义工具的注册信息:

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\CLSID\{E76D53CC-3D4F-40A2-BD4D-4F3419755476}]
    @="COM+ class: Microsoft.VSDesigner.CodeGenerator.TypedDataSourceGenerator.DataSourceGeneratorWrapper"
    "InprocServer32"="C:\\WINDOWS\\system32\\mscoree.dll"
    "ThreadingModel"="Both"
    "Class"="Microsoft.VSDesigner.CodeGenerator.TypedDataSourceGenerator.DataSourceGeneratorWrapper"
    "Assembly"="Microsoft.VSDesigner, Version=14.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a"
    
  2. 在特定语言的项目系统或服务定义的 GUID下Generators\GUID,在所需的 Visual Studio 配置单元中创建一个注册表项。GUID密钥的名称成为您的自定义工具的编程名称。自定义工具键具有以下值:

    • (Default)- 可选的。提供自定义工具的用户友好描述。此参数是可选的,但建议使用。

    • CLSID- 必需的。指定实现 IVsSingleFileGenerator 的 COM 组件的类库的标识符。

    • GeneratesDesignTimeSource- 必需的。指示此自定义工具生成的文件中的类型是否可供视觉设计人员使用。对于视觉设计师不可用的类型,此参数的值需要为(零)0,对于视觉设计师可用的类型,此参数的值需要为(一)1。

    请注意,您必须为您希望自定义工具可用的每种语言单独注册自定义工具。

    例如,MSDataSetGenerator为每种语言注册一次:

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{164b10b9-b200-11d0-8c61-00a0c91e29d5}\MSDataSetGenerator]
    @="Microsoft VB Code Generator for XSD"
    "CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
    "GeneratesDesignTimeSource"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{fae04ec1-301f-11d3-bf4b-00c04f79efbc}\MSDataSetGenerator]
    @="Microsoft C# Code Generator for XSD"
    "CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
    "GeneratesDesignTimeSource"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\Generators\{e6fdf8b0-f3d1-11d4-8576-0002a516ece8}\MSDataSetGenerator]
    @="Microsoft J# Code Generator for XSD"
    "CLSID"="{E76D53CC-3D4F-40a2-BD4D-4F3419755476}"
    "GeneratesDesignTimeSource"=dword:00000001
    
于 2016-08-24T17:12:02.867 回答