我需要一种从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);
还有另一种简化此方法的方法吗?我喜欢这种行为,但不喜欢这种风格! :)
非常感谢你!
答案 0 :(得分:2)
也许这会奏效(我没有测试过):
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需要是参考类型。
答案 1 :(得分:2)
问题是您可以从属性中推断出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);
}
接下来,使用Decorator pattern实现类型化数据记录(枯燥但直截了当):
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);
...