net - ¿Cómo convertir cadenas de archivos de recursos de C#a métodos y no solo a propiedades?
net core culture (2)
Por ejemplo, el proyecto EntityFramework Microsoft.EntityFrameworkCore.Relational tiene el siguiente texto en los archivos de recursos:
...
<data name="FromSqlMissingColumn" xml:space="preserve">
<value>The required column ''{column}'' was not present in the results of a ''FromSql'' operation.</value>
</data>
...
que genera el siguiente código 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;
}
...
Pero cuando edito el archivo en VS y lo guardo, obtengo solo propiedades simples generadas, como:
...
/// <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"); }
}
...
Los archivos en cuestión se pueden encontrar aquí:
Entonces la pregunta otra vez: ¿Cómo lo hicieron y cómo podría obtener el mismo resultado?
¿Cómo lo hicieron?
Primero, debería ser obvio que no usan el ResXFileCodeGenerator
estándar, sino una herramienta de generación de código personalizado.
Actualmente hay 2 formas estándar de generar código: la antigua usanza de la escuela con una Custom Tool
similar a ResXFileCodeGenerator
, o la manera moderna de usar una plantilla T4 . Así que deja ver.
La entrada correspondig dentro del archivo Microsoft.EntityFrameworkCore.Relational.csproj se ve así:
<ItemGroup>
<EmbeddedResource Include="Properties/RelationalStrings.resx">
<LogicalName>Microsoft.EntityFrameworkCore.Relational.Properties.RelationalStrings.resources</LogicalName>
</EmbeddedResource>
</ItemGroup>
Como podemos ver, definitivamente no usan la Custom Tool
.
Entonces debería ser una plantilla T4. Y, de hecho, justo después del artículo anterior, podemos ver:
<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>
¡Ahí vas!
Ahora, no sé cuál es el propósito del archivo xml
incluido sin sumergirse en la implementación (podría ser algo que el generador use, como las opciones o algo así), pero la generación del código real está contenida en los siguientes Recursos. tt archivo.
¿cómo podría obtener el mismo resultado?
Supongo que estás pidiendo tus propios proyectos. Bueno, puedes hacer algo similar. Seleccione su archivo resx , vaya a Properties
y borre la Custom Tool
. A continuación, agregue T4 template
a su proyecto y escriba la generación del código (no estoy seguro de si la licencia le permite usar su código, así que si quiere hacerlo, asegúrese de verificar primero si está permitido). Pero el principio sería el mismo.
Creo que el equipo EF usa una Custom Tool
personalizada Custom Tool
para ese propósito. Pero Visual Studio usa PublicResXFileCodeGenerator
como herramienta personalizada predeterminada para archivos .resx
y esta herramienta no tiene ninguna funcionalidad como PublicResXFileCodeGenerator
y su clase base ResXFileCodeGenerator
(ambas se pueden encontrar en Microsoft.VisualStudio.Design
ensamblado Microsoft.VisualStudio.Design
) es solo un contenedor para Visual Studio alrededor de StronglyTypedResourceBuilder
.
Se implementan IVsSingleFileGenerator
(ubicado en Microsoft.VisualStudio.Shell.Interop
assembly). Este es el lugar donde puede comenzar a implementar su propia Custom Tool
. Inicie la nueva Class Library
, agregue referencias Microsoft.VisualStudio.Shell.14.0
y Microsoft.VisualStudio.Shell.Interop
. Crea una nueva clase e implementa esta interfaz. Interface IVsSingleFileGenerator
es bastante simple. Contiene solo dos métodos:
DefaultExtension
que devuelve la extensión para el archivo generado (con un período principal) comoout string pbstrDefaultExtension
paratemer yVSConstant.S_OK
como valor de retorno (por supuesto, si todo está bien).Generate
cuál es aceptar:-
wszInputFilePath
: la ruta del archivo de entrada, puede ser nula, no la use. -
bstrInputFileContents
: se debe utilizar el contenido del archivo de entrada. -
wszDefaultNamespace
: espacio de nombres predeterminado (no puedo decir ahora por quéResXFileCodeGenerator
intercala con Visual Studio para obtener espacio de nombres en lugar de usar este parámetro). -
rgbOutputFileContents
: matriz de bytes del archivo generado. Debe incluir bytes de firma UNICODE o UTF-8 en la matriz de bytes devuelta, ya que esta es una secuencia sin formato. La memoria para rgbOutputFileContents debe asignarse utilizando la llamada de .NET Framework,Marshal.AllocCoTaskMem
, o la llamada al sistema Win32 equivalente,CoTaskMemAlloc
. El sistema del proyecto es responsable de liberar esta memoria. -
pcbOutput
: recuento de bytes en la matrizrgbOutputFileContent
. -
pGenerateProgress
: una referencia a la interfazIVsGeneratorProgress
través de la cual el generador puede informar su progreso al sistema del proyecto.
Y devuelve
VSConstant.S_OK
si todo está bien, o el código de error correspondiente.-
También hay una pequeña guía sobre la implementación . Pero esta guía no dice demasiado. Lo más útil es cómo registrar un generador propio.
Será mejor que te ResXFileCodeGenerator
en el código ResXFileCodeGenerator
(o simplemente descompile) para ver un ejemplo de implementación o para obtener algunos consejos, como cómo interoperar con Visual Studio. Pero no veo ninguna razón para interoperar con VS ya que todo lo que necesita lo ha proporcionado. .resx
contenido del archivo .resx
podría leerse en ResXResourceReader.FromFileContents
.
El resto sería simple ya que tiene nombres de recursos y valores, y solo necesita devolver una matriz de bytes del archivo generado. Creo que el análisis de los valores de recursos para tener un error de tiempo de compilación de formato no válido (por ejemplo: {{param}}}
) sería la mayor dificultad.
Cuando se analizan los valores y se encuentran los parámetros para su próximo método, puede generar código (de nuevo, como ejemplo, puede hacer referencia a ResXFileCodeGenerator
y StronglyTypedResourceBuilder
, o hacerlo usted mismo de la manera que desee, mediante CodeDom o redactar el código fuente de forma manual). Tampoco debería ser difícil ya que tiene un ejemplo de los métodos que necesita generar en la pregunta que publicó.
Compile su propio generador, regístrelo, .resx
propiedad Custom Tool
de sus archivos .resx
y obtendrá clases de recursos con métodos en lugar de propiedades.
También podrías compartirlo en github con otros. :)
Aquí hay instrucciones del registro de herramientas personalizadas (ya que el enlace msdn podría fallar pronto o más tarde):
Para que una herramienta personalizada esté disponible en Visual Studio, debe registrarla para que Visual Studio pueda instanciarla y asociarla a un tipo de proyecto en particular.
Registre la DLL de la herramienta personalizada en el registro local de Visual Studio o en el registro del sistema, en
HKEY_CLASSES_ROOT
.Por ejemplo, aquí está la información de registro para la herramienta personalizada
MSDataSetGenerator
administrada, que viene con 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"
Cree una clave de registro en la sección Visual Studio deseada en
Generators/GUID
dondeGUID
es el GUID definido por el sistema o servicio de proyecto del idioma específico. El nombre de la clave se convierte en el nombre programático de su herramienta personalizada. La clave de herramienta personalizada tiene los siguientes valores:(Default)
- Opcional. Proporciona una descripción fácil de usar de la herramienta personalizada. Este parámetro es opcional, pero recomendado.CLSID
- Obligatorio. Especifica el identificador de la biblioteca de clases del componente COM que implementa IVsSingleFileGenerator.GeneratesDesignTimeSource
- Obligatorio. Indica si los tipos de archivos producidos por esta herramienta personalizada están disponibles para los diseñadores visuales. El valor de este parámetro debe ser (cero) 0 para los tipos que no están disponibles para los diseñadores visuales o (uno) 1 para los tipos disponibles para los diseñadores visuales.
Tenga en cuenta que debe registrar la herramienta personalizada por separado para cada idioma para el que desea que esté disponible la herramienta personalizada.
Por ejemplo,
MSDataSetGenerator
registra una vez para cada idioma:[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