Genere y compile nombres para indexar traducción/mapeo para una reutilización más rápida

StackOverflow https://stackoverflow.com//questions/11665503

  •  11-12-2019
  •  | 
  •  

Pregunta

Supongamos que obtengo datos de un servicio (que no puedo controlar) como:

public class Data
{
    // an array of column names
    public string[] ColumnNames { get; set; }

    // an array of rows that contain arrays of strings as column values
    public string[][] Rows { get; get; }
}

y en el nivel medio me gustaría mapear/traducir esto a un IEnumerable<Entity> donde los nombres de las columnas en Data tal vez representados como propiedades en mi Entity clase.Yo dije puede porque es posible que no necesite todos los datos devueltos por el servicio sino solo algunos de ellos.

Transformación

Esta es una abstracción de un algoritmo que haría la traducción:

  1. crear un IDictionary<string, int> de ColumnNames por lo que puedo asignar fácilmente nombres de columnas individuales a índices de matriz en filas individuales.
  2. Usar la reflexión para examinar mi Entity nombres de propiedades para poder relacionarlos con nombres de columnas
  3. iterar a través de Data.Rows y crear mi Entity objetos y completar propiedades de acuerdo con el mapeo realizado en el n.° 1.Probablemente usando reflexión y SetValue en las propiedades para configurarlas.

Mejoramiento

El algoritmo superior, por supuesto, funcionaría, pero creo que debido a que utiliza reflexión debería realizar algo de almacenamiento en caché y posiblemente algo de compilación sobre la marcha, lo que podría acelerar las cosas considerablemente.

Cuando finalicen los pasos 1 y 2, podríamos generar un método que tome una matriz de cadenas y cree instancias de mis entidades usando índices directamente y compilarlo. y almacenarlo en caché para su futura reutilización.

Normalmente obtengo una página de resultados, por lo que las solicitudes posteriores reutilizarían el mismo método compilado.

Dato adicional

Esto no es imperativo para la pregunta (y las respuestas), pero también creé dos atributos que ayudan con la asignación de columna a propiedad cuando no coinciden en los nombres.Creé lo más obvio MapNameAttribute (que toma una cadena y opcionalmente también habilita la distinción entre mayúsculas y minúsculas) y IgnoreMappingAttribute para propiedades en mi Entity eso no debería asignarse a ningún dato.Pero estos atributos se leen en el paso 2 del algoritmo superior, por lo que los nombres de las propiedades se recopilan y se les cambia el nombre de acuerdo con estos metadatos declarativos para que coincidan con los nombres de las columnas.

Pregunta

¿Cuál es la mejor y más sencilla forma de generar y compilar dicho método?¿Expresiones lambda? CSharpCodeProvider ¿clase?

¿Quizás tenga un ejemplo de código generado y compilado que haga algo similar?Supongo que los mapeos son un escenario bastante común.

Nota:Mientras tanto, examinaré PetaPoco (y tal vez también Massive) porque, que yo sepa, ambos compilan y almacenan en caché sobre la marcha exactamente con fines de mapeo.

¿Fue útil?

Solución

Sugerencia: obtener FastMember de NuGet

Entonces solo usa:

var accessor = TypeAccessor.Create(typeof(Entity));

Luego, justo en tu bucle, cuando hayas encontrado el memberName y newValue para la iteración actual:

accessor[obj, memberName] = newValue;

Esto está diseñado para hacer lo que usted pide;Internamente, mantiene un conjunto de tipos si se ha visto antes.Cuando se ve un nuevo tipo, se crea una nueva subclase de TypeAccessor sobre la marcha (a través de TypeBuilder) y lo almacena en caché.Cada uno único TypeAccessor es consciente de las propiedades para ese tipo, y básicamente simplemente actúa como un:

switch(memberName) {
    case "Foo": obj.Foo = (int)newValue;
    case "Bar": obj.Bar = (string)newValue;
    // etc
}

Debido a que esto está almacenado en caché, usted sólo paga cualquier costo (y no realmente un grande costo) la primera vez que ve tu tipo;el resto del tiempo es gratis.porque usa ILGenerator directamente, también evita cualquier abstracción innecesaria, por ejemplo mediante Expression o CodeDom, por lo que es lo más rápido posible.

(También debo aclarar que para dynamic tipos, es decirtipos que implementan IDynamicMetaObjectProvider, puede usar una única instancia para admitir cada objeto).


Adicional:

Lo que tu podría hacer es:tomar lo existente FastMember código y edítelo para procesarlo. MapNameAttribute y IgnoreMappingAttribute durante WriteGetter y WriteSetter;Entonces todo el vudú sucede en tu datos nombres, en lugar de miembro nombres.

Esto significaría cambiar las líneas:

il.Emit(OpCodes.Ldstr, prop.Name);

y

il.Emit(OpCodes.Ldstr, field.Name);

en ambos WriteGetter y WriteSetter, y haciendo un continue al inicio del foreach bucles si se debe ignorar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top