65.9K
CodeProject 正在变化。 阅读更多。
Home

具有 ILGenerator 的通用数据读取器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.48/5 (12投票s)

2007年3月1日

2分钟阅读

viewsIcon

44327

downloadIcon

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 的人告诉我,他们节省了大量的数据访问层编码时间。

© . All rights reserved.