2 Stimmen

Besserer Weg, diese Methode mit Lambda-Ausdruck und Generika zu schreiben

Ich brauche eine Methode zum Abrufen von Daten aus einem IDataRecord ohne die lästigen "magischen Zeichenfolgen", um Feldnamen auszudrücken. Nach einigen Recherchen habe ich diese Erweiterungsmethode gefunden:

public static T2 ReadValue<T1, T2>(this IDataRecord record, Expression<Func<T1, T2>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    string fieldName = body.Member.Name;
    int ordinal = record.GetOrdinal(fieldName);
    return (T2)(record.IsDBNull(ordinal) ? default(T2) : record.GetValue(ordinal));
}

Und dann verwende ich diese Methode so:

product.Name = record.ReadValue<Product, string>(p => p.Name);

Gibt es eine andere Möglichkeit, diese Methode zu vereinfachen? Ich mag das Verhalten, aber nicht den Stil! :)

Vielen, vielen Dank!

2voto

Henrik Punkte 22966

Vielleicht würde das funktionieren (ich habe es nicht getestet):

public static void ReadValue<T1, T2>(this IDataRecord record, T1 product, Expression<Func<T1, T2>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    string fieldName = body.Member.Name;
    int ordinal = record.GetOrdinal(fieldName);
    var val = (T2)(record.IsDBNull(ordinal) ? default(T2) : record.GetValue(ordinal));
    ((PropertyInfo)body.Member).SetValue(product, val, null);
} 

Dann könnte man es so nennen:

record.ReadValue(product, p => p.Name); 

Der Name wird nur einmal angegeben, der Compiler leitet daraus die generischen Typen ab. Offensichtlich muss T1 ein Referenztyp sein.

2voto

Bryan Watts Punkte 43539

Das Problem ist, dass Sie die string Ergebnistyp aus der Eigenschaft, aber Sie müssen ihn trotzdem in den Typargumenten angeben, weil Sie auch die Product .

Eine gute Möglichkeit, dies zu erreichen, wäre ein typisierter Datensatz:

IDataRecord<Product> productRecord = ...;

string name = productRecord.ReadValue(p => p.Name);

Dies scheint machbar zu sein, denn in einem ORM-Kontext wie dem, den Sie meinen, sollten Sie den von einem Datensatz repräsentierten Datentyp kennen.

Der schwierige Teil ist die ... im obigen Code. Er erfordert eine gewisse Infrastruktur, aber Sie müssen ihn nur einmal schreiben und können ihn überall verwenden. Der erste Schritt besteht darin, den typisierten Datensatz abzuleiten:

public interface IDataRecord<T> : IDataRecord
{
    TValue GetValue<TValue>(Expression<Func<T, TValue>> getter);
}

Als nächstes implementieren Sie den getippten Datensatz mit der Dekorationsmuster (langweilig, aber einfach):

public class DataRecord<T> : IDataRecord<T>
{
    private readonly IDataRecord _untypedRecord;

    public DataRecord(IDataRecord untypedRecord)
    {
        _untypedRecord = untypedRecord;
    }

    public TValue GetValue<TValue>(Expression<Func<T, TValue>> getter)
    {
        ...the original code...
    }

    ...pass through all other members to the untyped record...
}

Schließlich fügen Sie eine Transformation vom untypisierten zum typisierten Datensatz hinzu:

public static class TypedDataRecords
{
    public static IDataRecord<T> TypedAs<T>(this IDataRecord untypedRecord)
    {
        return new DataRecord<T>(untypedRecord);
    }
}

Das Beispiel sieht folgendermaßen aus:

IDataRecord<Product> productRecord = record.TypedAs<Product>();

string name = productRecord.ReadValue(p => p.Name);
decimal price = productRecord.ReadValue(p => p.Price);
...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X