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 
    
相關問題