12

例如,的EntityFramework Microsoft.EntityFrameworkCore.Relational项目在资源文件下面的文字:如何将C#资源文件字符串转换为方法而不仅仅是属性?

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

有问题的文件可以找到他再次:

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

回答

6

他们是怎么做到的?

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

目前有两种标准的代码生成方式 - 使用类似于ResXFileCodeGeneratorCustom Tool或使用T4 Template的现代方式。所以让我们看看。

Microsoft.EntityFrameworkCore.Relational.csproj文件里,correspondig条目看起来是这样的:

<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添加到您的项目并编写代码生成(我不确定许可证是否允许您使用他们的代码,所以如果您想这样做,请确保您首先检查它是否被允许)。但原则是一样的。

4

我想EF团队使用自己定制的Custom Tool来达到这个目的。但视觉工作室使用PublicResXFileCodeGenerator作为.resx文件的默认自定义工具,并且此工具没有PublicResXFileCodeGenerator和它的基类ResXFileCodeGenerator(均可在Microsoft.VisualStudio.Design汇编中找到)的功能仅仅是用于围绕StronglyTypedResourceBuilder的视觉工作室的包装。

它们实现IVsSingleFileGenerator(位于Microsoft.VisualStudio.Shell.Interop程序集中)。所以这是你可以开始实施你自己的Custom Tool的地方。开始新的Class Library,添加Microsoft.VisualStudio.Shell.14.0Microsoft.VisualStudio.Shell.Interop参考。创建新的类并实现这个接口。界面IVsSingleFileGenerator非常简单。它仅包含两个方法:

  • DefaultExtension返回扩展生成文件(带有超前时段)为out string pbstrDefaultExtension paratemer和VSConstant.S_OK作为返回值(当然,如果一切OK)。

  • Generate这是接受:

    • wszInputFilePath - 输入文件路径,可以为空,不使用它。
    • bstrInputFileContents - 应使用输入文件内容。
    • wszDefaultNamespace - 默认名称空间(现在无法分辨为什么ResXFileCodeGenerator与Visual Studio进行交互以获取名称空间而不是使用此参数)。
    • rgbOutputFileContents - 生成文件的字节数组。您必须在返回的字节数组中包含UNICODE或UTF-8签名字节,因为这是一个原始数据流。 rgbOutputFileContents的内存必须使用.NET Framework调用Marshal.AllocCoTaskMem或等效的Win32系统调用CoTaskMemAlloc进行分配。项目系统负责释放这些内存。
    • pcbOutput - rgbOutputFileContent数组中的字节数。
    • pGenerateProgress - 提及IVsGeneratorProgress接口,通过该接口,发电机可以将其进展报告给项目系统。

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

另外还有small guide about implementation。但是这个指南并没有讲太多。最有用的东西是how to register自己的发电机。

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

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

当值分析,并找到了适合自己即将到来的方法的参数就可以生成代码(同样作为一个例子,你可以参考ResXFileCodeGeneratorStronglyTypedResourceBuilder,或由你自己做你想要的方式,通过CodeDom中或手动编写源代码的文本)。这也不应该是困难的,因为您已经有一个您需要在发布的问题中生成的方法示例。

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

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


这里是custom tool registration指令(如MSDN链接可能迟早死):

要在Visual Studio中提供的自定义工具,你必须注册,以便Visual Studio可以对其进行实例化和将其与特定的项目类型相关联。

  1. 注册自定义工具DLL无论是在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. 创建Generators\GUID下所需的Visual Studio配置单元的注册表项,其中GUID是定义的GUID由特定语言的项目系统或服务提供。密钥的名称成为您的自定义工具的编程名称。自定义工具键具有以下值:

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

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

    • GeneratesDesignTimeSource - 必填。指示是否将此自定义工具生成的文件的类型提供给可视设计人员。对于可视设计人员不可用的类型,此参数的值必须为(零)0;对于可视设计人员可用的类型,此参数的值必须为(零)0。

    请注意,您必须为每种语言单独注册自定义工具,以便使自定义工具可用。

    例如,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 
    
相关问题