Generare e di compilare il nome di indice di traduzione e/o la mappatura per velocizzare la riutilizzabilità
-
11-12-2019 - |
Domanda
Supponiamo di ottenere i dati da un servizio (che non riesco a controllare) come:
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; }
}
e nel livello intermedio vorrei mappa/tradurre questo per un IEnumerable<Entity>
dove i nomi delle colonne Data
può essere rappresentato come nella mia proprietà Entity
classe.Ho detto può perché io non bisogno di tutti i dati restituiti dal servizio, ma solo una parte.
Trasformazione
Questa è un'astrazione di un algoritmo che vorresti fare la traduzione:
- creare un
IDictionary<string, int>
diColumnNames
quindi posso facilmente sulla mappa i nomi delle singole colonne di indici di un array in singole righe. - utilizzare la reflection per il mio esame di
Entity
proprietà' nomi quindi sono in grado di abbinare con i nomi di colonna - scorrere
Data.Rows
e creare il mioEntity
oggetti e popolare proprietà secondo la mappatura fatta in #1.Probabile utilizzo di riflessione e diSetValue
sulla proprietà per impostare loro.
Ottimizzazione
Superiore algoritmo del lavoro di corso, ma penso che a causa di esso utilizza la riflessione si dovrebbe fare un po 'di cache e forse un po' al volo compilation, che potrebbe accelerare le cose notevolmente.
Quando i passaggi 1 e 2 sono fatto, si potrebbe effettivamente generare un metodo che accetta un array di stringhe e crea il mio entità utilizzando indici direttamente e compilarlo e cache per poterlo riutilizzare in futuro.
Io sono di solito una pagina di risultati, quindi, le successive richieste di riutilizzare la stessa compilato metodo.
Ulteriori effetti
Questo non è un imperativo per la domanda (e risposta), ma ho anche creato due attributi che aiutano con la colonna di mapping di proprietà quando questi non corrispondono a nomi.Ho creato la più ovvia MapNameAttribute
(che prende una stringa e, facoltativamente, anche abilitare il caso di sensibilità) e IgnoreMappingAttribute
per le proprietà sul mio Entity
che non dovrebbe mappa di tutti i dati.Ma questi attributi sono leggere nel passaggio 2 della superiore algoritmo così i nomi di proprietà sono raccolti e rinominato secondo questo dichiarativa di metadati in modo che corrispondano i nomi di colonna.
Domanda
Qual è il modo migliore e più semplice per generare e compilare tale metodo?Le espressioni Lambda? CSharpCodeProvider
classe?
Non è forse un esempio di generazione di e ha compilato il codice che fa una cosa simile?Credo che le mappature sono piuttosto scenario comune.
Nota:Nel frattempo ho deciso PetaPoco (e forse anche di massa) perché, per quanto ne so fanno entrambi la compilazione e la memorizzazione nella cache di volare esattamente per la mappatura scopi.
Soluzione
Suggerimento: ottenere FastMember da NuGet
Poi basta usare:
var accessor = TypeAccessor.Create(typeof(Entity));
Quindi solo nel ciclo, quando hai trovato l' memberName
e newValue
per l'iterazione corrente:
accessor[obj, memberName] = newValue;
Questo è stato progettato per fare quello che stai chiedendo;internamente, mantiene un insieme di tipi se ha mai visto prima.Quando un nuovo tipo di visto, crea una nuova sottoclasse di TypeAccessor
on-the-fly (via TypeBuilder
) e lo memorizza nella cache.Unica ogni TypeAccessor
è a conoscenza delle proprietà di quel tipo, e fondamentalmente solo agisce come un:
switch(memberName) {
case "Foo": obj.Foo = (int)newValue;
case "Bar": obj.Bar = (string)newValue;
// etc
}
Perché questo è nella cache, è solo pagare alcun costo (e non è un grande il costo) la prima volta che si vede mai il vostro tipo;il resto del tempo, è libero.Perché utilizza ILGenerator
direttamente, inoltre, evita inutili astrazione, per esempio via Expression
o CodeDom, quindi è circa veloce come si può.
(Devo anche precisare che per la dynamic
tipi, vale a diretipi che implementano IDynamicMetaObjectProvider
, è possibile utilizzare una singola istanza di supporto ogni oggetto).
Aggiuntive:
Che cosa è potrebbe fare è:prendere l'esistente FastMember
codice e modificarlo a processo MapNameAttribute
e IgnoreMappingAttribute
durante WriteGetter
e WriteSetter
;quindi tutti i voodoo accade sul vostro dati nomi, piuttosto che il membro nomi.
Questo significherebbe cambiare le linee:
il.Emit(OpCodes.Ldstr, prop.Name);
e
il.Emit(OpCodes.Ldstr, field.Name);
in entrambi i WriteGetter
e WriteSetter
, e facendo un continue
all'inizio del foreach
loop se dovrebbe essere ignorato.