Generieren und kompilieren Sie die Namen-zu-Index-Übersetzung/-Zuordnung für eine schnellere Wiederverwendbarkeit

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

  •  11-12-2019
  •  | 
  •  

Frage

Angenommen, ich erhalte Daten von einem Dienst (den ich nicht kontrollieren kann) als:

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; }
}

und auf der mittleren Ebene möchte ich dies einem zuordnen/übersetzen IEnumerable<Entity> wo Spaltennamen drin sind Data Vielleicht in meinem als Eigenschaften dargestellt Entity Klasse.Ich sagte Mai weil ich möglicherweise nicht alle vom Dienst zurückgegebenen Daten benötige, sondern nur einige davon.

Transformation

Dies ist eine Abstraktion eines Algorithmus, der die Übersetzung durchführen würde:

  1. Erstelle ein IDictionary<string, int> von ColumnNames So kann ich einzelne Spaltennamen problemlos Array-Indizes in einzelnen Zeilen zuordnen.
  2. Benutze Reflexion, um meine zu untersuchen Entity Eigenschaftennamen, damit ich sie mit Spaltennamen abgleichen kann
  3. iterieren Data.Rows und erschaffe mein Entity Objekte und füllen Sie Eigenschaften gemäß der in Nr. 1 durchgeführten Zuordnung.Wahrscheinlich mit Reflexion und SetValue auf Eigenschaften, um sie festzulegen.

Optimierung

Der obere Algorithmus würde natürlich funktionieren, aber ich denke, dass er aufgrund der Verwendung von Reflektion etwas Caching und möglicherweise eine spontane Kompilierung durchführen sollte, was die Dinge erheblich beschleunigen könnte.

Wenn die Schritte 1 und 2 abgeschlossen sind, könnten wir tatsächlich eine Methode generieren, die ein Array von Zeichenfolgen verwendet, meine Entitäten mithilfe von Indizes direkt instanziiert und sie kompiliert und zwischenspeichern für eine spätere Wiederverwendung.

Normalerweise erhalte ich eine Ergebnisseite, sodass nachfolgende Anfragen dieselbe kompilierte Methode wiederverwenden würden.

Zusätzlicher Fakt

Dies ist für die Frage (und Antworten) nicht zwingend erforderlich, aber ich habe auch zwei Attribute erstellt, die bei der Zuordnung von Spalten zu Eigenschaften helfen, wenn diese in ihren Namen nicht übereinstimmen.Ich habe das Offensichtlichste geschaffen MapNameAttribute (das benötigt eine Zeichenfolge und aktiviert optional auch die Groß-/Kleinschreibung) und IgnoreMappingAttribute für Immobilien auf meinem Entity das sollte keinen Daten zugeordnet werden.Diese Attribute werden jedoch in Schritt 2 des oberen Algorithmus gelesen, sodass Eigenschaftsnamen gemäß diesen deklarativen Metadaten gesammelt und umbenannt werden, sodass sie mit den Spaltennamen übereinstimmen.

Frage

Was ist der beste und einfachste Weg, eine solche Methode zu generieren und zu kompilieren?Lambda-Ausdrücke? CSharpCodeProvider Klasse?

Haben Sie vielleicht ein Beispiel für generierten und kompilierten Code, der etwas Ähnliches bewirkt?Ich vermute, dass Zuordnungen ein ziemlich häufiges Szenario sind.

Notiz:In der Zwischenzeit werde ich PetaPoco (und vielleicht auch Massive) untersuchen, weil beide afaik, im laufenden Betrieb, genau zu Kartierungszwecken kompilieren und zwischenspeichern.

War es hilfreich?

Lösung

Anregung: Erhalten Sie FastMember von NuGet

Dann verwenden Sie einfach:

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

Dann einfach in deiner Schleife, wenn du das gefunden hast memberName Und newValue für die aktuelle Iteration:

accessor[obj, memberName] = newValue;

Dies soll das tun, was Sie verlangen;Intern verwaltet es eine Reihe von Typen, sofern diese zuvor gesehen wurden.Wenn ein neuer Typ angezeigt wird, wird eine neue Unterklasse erstellt TypeAccessor spontan (via TypeBuilder) und speichert es zwischen.Jedes einzigartig TypeAccessor kennt die Eigenschaften für diesen Typ und grundsätzlich verhält sich einfach wie ein:

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

Da dies zwischengespeichert wird, zahlen Sie nur etwaige Kosten (und nicht wirklich einen groß Kosten) das erste Mal, dass es Ihren Typ sieht;die restliche Zeit ist es kostenlos.Weil es nutzt ILGenerator direkt, es vermeidet auch jede unnötige Abstraktion, zum Beispiel via Expression oder CodeDom, also ist es so schnell wie möglich.

(Ich sollte das auch klarstellen dynamic Typen, d.h.Typen, die implementieren IDynamicMetaObjectProvider, es kann eine einzelne Instanz verwenden, um jedes Objekt zu unterstützen).


Zusätzlich:

Was du könnte tun ist:Nimm das Vorhandene FastMember Code eingeben und zur Verarbeitung bearbeiten MapNameAttribute Und IgnoreMappingAttribute während WriteGetter Und WriteSetter;Dann passiert der ganze Voodoo auf deinem Daten Namen, statt der Mitglied Namen.

Dies würde bedeuten, die Zeilen zu ändern:

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

Und

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

sowohl WriteGetter Und WriteSetter, und tun a continue zu Beginn des foreach Schleifen, wenn es ignoriert werden soll.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top