具有 ILGenerator 的通用数据读取器






3.48/5 (12投票s)
2007年3月1日
2分钟阅读

44327

808
在运行时生成一个委托,从 DataReader 中读取数据。通过反射,可以将一个简单的查询直接绑定到一个对象列表。
引言
在进行数据访问层开发时,你是否曾经想过能够接收一个对象列表而不是一个 DataReader
。这可以通过反射和代码生成来实现。这节省了大量时间,如果你认为这种方法比传统方法慢,那你就错了。性能与传统方法大致相同。
背景
上次我处理数据访问层时,我发现自己一遍又一遍地重复相同的代码行。我执行了一个 DataReader
并在 while 语句中读取每一列。这种方法会导致许多运行时错误。我们必须记住每一列的索引。如果我们在一开始添加一列而不更改索引会怎样?所以我开发了一种将类绑定到查询的方法。通过反射分析类,我们能够自动生成使用数据读取器的代码。
我做了一个小项目来测试 GenericReader
的性能。为此,我实现了一个 IDataReader
,它仅在内存中工作,以便获得最可靠的基准测试。
使用代码
为了做到这一点,我们可以使用反射。 想法是将一个字段绑定到 select 语句的一列。
例如,我声明了一个包含 4 个字段的类。每个字段都有一个带有列名的属性。我们可以看到这些字段是只读的;这可以通过 IL 代码实现。此外,我们可以看到我使用了一个 double?
来表示一个值类型可以为 null。验证是使用 DBNull
完成的,并且程序将值设置为 null
。这更容易使用。
public struct TestClass
{
[ReaderName("Field1")]
public readonly long Field1;
[ReaderName("Field2")]
public readonly string Field2;
[ReaderName("Field3")]
public readonly double? Field3;
[ReaderName("Field4")]
public readonly bool Field4;
}
我们需要做的最后一件事是使用 GenericReader
。以下方法让我们看看它有多么容易。
GenericReader genericReader = new GenericReader();
private List<testclass /> ReaderExample(SqlConnection connection)
{
SqlCommand command = new SqlCommand("SELECT Field1, Field2, Field3, Field4 " +
"FROM AnyTable");
return genericReader.ReadReader<testclass />(command.ExecuteReader());
}
GenericReader
在我们第一次使用类型时使用 codeIL 生成一个委托。该委托被反复用于读取 DataReader
。
关注点
最有用的事情是代码更容易维护。特别是在我们需要在 select 语句中添加信息时。在这种情况下,在使用 DataReader 的索引时很容易犯错误。
历史
起初,我使用反射来读取数据,而不是使用 codeIL 创建一个委托。很快就需要一种更快的方法。所以我稍微改变了一下。CodeIL 实现起来要困难得多,但这是值得的。使用 GenericReader
的人告诉我,他们节省了大量的数据访问层编码时间。