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

用 DynamicSqlDataReader 替换 SqlDataReader

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.73/5 (5投票s)

2009年5月31日

CPOL
viewsIcon

25201

使用 .NET 4.0 的动态特性,从你的应用程序中移除这些字符串。

引言

这是一个非常简单的例子,展示了 .NET 4.0 的动态特性如何在类型化和非类型化之间提供帮助。它将 SqlDataReader 包装在 DynamcSqlDataReader 中,允许你将字段作为属性访问,而不是将包含列名的字符串值传递给方法。

使用代码

有些代码读起来像诗歌,不需要过多的解释。通过查看测试用例,你应该能够了解其预期用途。用于证明概念的数据库包含一个名为 Person 的表,其中包含以下字段:

  • Id: uniqueidentifier (PK)
  • Name: nvarchar(50) 非空
  • Country: nvarchar(50) 可空
  • Age: smallint 可空

测试用例

[TestClass]
public class PersonDataAccessTests
{
    public void Theory_Read_Person_Table(Action<dynamic> assertion)
    {
        using (var connection = new SqlConnection(@"CONNECTION STRING HERE"))
        {
            using (var command = new SqlCommand("SELECT TOP 1 Id," + 
                   "Name,Country,Age FROM Person", connection))
            {
                connection.Open();

                var x = new DynamicSqlDataReader(command.ExecuteReader(
                            System.Data.CommandBehavior.CloseConnection));
                while (x.Read())
                {
                    assertion(x);
                }
            }
        }
    }

    [TestMethod]
    [ExpectedException(typeof(ColumnIsNotInResultSetException))]
    public void Test_Read_Person_Table_Failure()
    {
        Theory_Read_Person_Table((x) =>
        {
            string s = x.NonExistingColumn;
        });
    }

    [TestMethod]
    public void Test_Read_Person_Table_Success()
    {
        Theory_Read_Person_Table((x) =>
        {
            Guid id = x.Id;
            string name = x.Name;
            string country = x.Country;
            int? age = x.Age;

            Assert.AreNotEqual(Guid.Empty, id);
            Assert.AreEqual("Tim", name);
            Assert.AreEqual("Belgium", country);
            Assert.AreEqual(null, age);
        });
    }
}

实际代码

public class DynamicSqlDataReader : DynamicObject
{
    private SqlDataReader reader;

    public DynamicSqlDataReader(SqlDataReader reader)
    {
        this.reader = reader;
    }

    public bool Read()
    {
        return reader != null && (!reader.IsClosed) && reader.Read();
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        try
        {
            var rawResult = reader.GetValue(reader.GetOrdinal(binder.Name));
            result = rawResult == DBNull.Value ? null : rawResult;
            return true;
        }
        catch (IndexOutOfRangeException)
        {
            throw new ColumnIsNotInResultSetException(binder.Name);
        }
    }
}

关注点

请提供一些反馈,并讨论如何使用新的动态特性。 我也知道,将 DbDataReader 而不是具体的 SqlDataReader 包装起来会更干净,但仅仅是为了展示新的动态特性的使用 :)

© . All rights reserved.