Лучший способ написать этот метод с использованием лямбда-выражения и обобщений - PullRequest
2 голосов
/ 31 августа 2011

Мне нужен метод для извлечения данных из IDataRecord без использования назойливых «волшебных строк» ​​для выражения имен полей.После некоторых исследований я придумал этот метод расширения:

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

И затем я использую этот метод следующим образом:

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

Есть ли другой способ упростить этот метод?Мне нравится поведение, но не стиль!:)

Большое спасибо!

Ответы [ 2 ]

2 голосов
/ 31 августа 2011

Проблема в том, что вы можете вывести тип результата string из свойства, но вам все равно придется указать его в аргументах типа, потому что вы также должны указать Product.

Хороший способ приблизиться к этому - это типизированная запись данных:

IDataRecord<Product> productRecord = ...;

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

Это кажется возможным, потому что в контексте ORM, подобном тому, который вы подразумеваете, вы должны знать тип данных, представленный записью.

Самая сложная часть - это ... в приведенном выше коде. Это требует некоторой инфраструктуры, но вы должны написать ее только один раз, и вы можете использовать ее везде. Первым шагом является получение типизированной записи данных:

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

Затем реализуйте запись типизированных данных, используя шаблон Декоратор (скучно, но прямо):

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

Наконец, добавьте преобразование из нетипизированной в типизированную запись:

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

Теперь пример выглядит так:

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

string name = productRecord.ReadValue(p => p.Name);
decimal price = productRecord.ReadValue(p => p.Price);
...
2 голосов
/ 31 августа 2011

Может быть, это сработает (я не проверял):

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

Тогда вы могли бы назвать это так:

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

Имя указывается только один раз, компилятор выводит универсальные типы. Очевидно, что T1 должен быть ссылочным типом.

...