A Better DataReader for C# 2.0

在使用 ADO.NET 的 DataReader 來讀取欄位資料時,常常要寫很多判斷欄位值是否為 DBNull 的程式碼,例如:

SqlConnection cn = new SqlConnection("連線字串");
SqlCommand cmd = new SqlCommand("SELECT * FROM ...", cn);
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
if (rdr.IsDBNull(rdr.GetOrdinal("BIRTHDAY")))
{
Response.Write("");
}
else
{
Response.Write(Convert.ToDateTime(rdr["BIRTHDAY"], "yyyy-MM-dd"));
}
}
若不先判斷欄位值是否為 DBNull,程式執行時就會出現資料轉換失敗的 exception。如果能這樣寫就方便多了:
 SqlConnection cn = new SqlConnection("連線字串");
SqlCommand cmd = new SqlCommand("SELECT * FROM ...", cn);
BetterDataReader rdr = new BetterDataReader(cmd.ExecuteReader());
while (rdr.Read())
{
Response.Write(rdr.GetDateTimeStr("BIRTHDAY"));
}
這裡的 BetterDataReader 是修改自 Steve Michelotti 的 Nullable Data Readers,它本身雖然也實作了 IDataReader 介面,但大部分的實作方法都是直接呼叫外界傳入的 DataReader 物件的既有方法,同時再增加我們需要的方法。換句話說,BetterDataReader 只是一個簡單的 DataReader 轉換器(adapter)而已,它使用 wrapper(而非繼承)的方式來補強既有類別不足的地方,主要原因是既有的 DataReader 類別並不允許繼承。這是 C# 2.0 的解法,如果是 C# 3.0,就可以用 extension methods,這樣在撰寫程式時就更直覺了。

在剛才的範例程式中,GetDateTimeStr 方法會將你指定的日期欄位值轉換成字串傳回,若欄位值為 DBNull,則傳回空字串。這個方法便可以省掉每次判斷 DBNull 的瑣碎工作。

以下是 BetterDataReader 的部分原始碼:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Huanlin.Helper;

namespace Huanlin.Data
{
/// <summary>
/// 此類別是 DataReader 物件的簡單包裝,主要在解決欄位值為 DBNull 的問題,並增加一些方便的取值方法。
/// </summary>
public class BetterDataReader : IDataReader
{
#region Private Fields

IDataReader reader;

/// <summary>
/// Delegate to be used for anonymous method delegate inference
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
private delegate T Conversion<T>(int ordinal);

#endregion

#region Private Methods

/// <summary>
/// This generic method will be call by every interface method in the class.
/// The generic method will offer significantly less code, with type-safety.
/// Additionally, the methods can you delegate inference to pass the
/// appropriate delegate to be executed in this method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ordinal">Column index.</param>
/// <param name="convert">Delegate to invoke if the value is not DBNull</param>
/// <returns></returns>
private Nullable<T> GetNullable<T>(int ordinal, Conversion<T> convert) where T : struct
{
Nullable<T> nullable;
if (reader.IsDBNull(ordinal))
{
nullable = null;
}
else
{
nullable = convert(ordinal);
}
return nullable;
}

#endregion

#region Constructors

/// <summary>
/// 建構函式。
/// </summary>
/// <param name="dataReader"></param>
public BetterDataReader(IDataReader dataReader)
{
reader = dataReader;
}

#endregion

#region IDataReader Members
// (略)
#endregion

#region IDisposable Members
// (略)
#endregion


#region IDataRecord Members

public DateTime GetDateTime(int i)
{
return reader.GetDateTime(i);
}

public DateTime GetDateTime(string name)
{
return this.reader.GetDateTime(reader.GetOrdinal(name));
}

/// <summary>
/// 取得可為 NULL 的 DateTime 物件。
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Nullable<DateTime> GetNullableDateTime(int index)
{
return GetNullable<DateTime>(index, GetDateTime);
}

/// <summary>
/// 取得可為 NULL 的 DateTime 物件。
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Nullable<DateTime> GetNullableDateTime(string name)
{
return GetNullableDateTime(reader.GetOrdinal(name));
}
/// <summary>
/// 傳回格式化的日期時間字串。若欄位值為 NULL,則傳回空字串。
/// </summary>
/// <param name="name">欄位名稱。</param>
/// <param name="format">格式化字串。</param>
/// <returns>日期時間字串。</returns>
public string GetDateTimeStr(string name, string format)
{
Nullable<DateTime> dt = GetNullableDateTime(name);
if (dt.HasValue)
{
return dt.Value.ToString(format);
}
return "";
}

/// <summary>
/// 傳回格式化的日期時間字串。
/// </summary>
/// <param name="name">欄位名稱。</param>
/// <returns>日期時間字串。</returns>
public string GetDateTimeStr(string name)
{
return GetDateTimeStr(name, DateTimeHelper.DateTimeFormat);
}

/// <summary>
/// 傳回格式化的日期字串。若欄位值為 NULL,則傳回空字串。
/// </summary>
/// <param name="name">欄位名稱。</param>
/// <returns>日期時間字串。</returns>
public string GetDateStr(string name)
{
return GetDateTimeStr(name, DateTimeHelper.DateFormat);
}

public int GetInt32(int i)
{
if (reader.IsDBNull(i))
return 0;
return reader.GetInt32(i);
}

public int GetInt32(string name)
{
return this.GetInt32(reader.GetOrdinal(name));
}

/// <summary>
/// 若欄位值為 NULL,則傳回預設值。
/// </summary>
/// <param name="name">欄位名稱。</param>
/// <param name="defaultValue">預設值。</param>
/// <returns>欄位值。</returns>
public int GetInt32(string name, int defaultValue)
{
int index = reader.GetOrdinal(name);
if (reader.IsDBNull(index))
{
return defaultValue;
}
return reader.GetInt32(index);
}

public string GetString(int i)
{
if (reader.IsDBNull(i))
return "";
return reader.GetString(i);
}

public string GetString(string name)
{
return this.GetString(reader.GetOrdinal(name));
}

/// <summary>
/// 若欄位值為 NULL 或空字串(包含 Tab、換行字元),則傳回預設值。
/// </summary>
/// <param name="name">欄位名稱。</param>
/// <param name="defaultValue">預設值。</param>
/// <returns>欄位值。</returns>
public string GetString(string name, string defaultValue)
{
string value = this.GetString(name);
if (StrHelper.IsEmpty(value))
{
return defaultValue;
}
return value;
}
#endregion
}
}
Copyright © 2012. Huan-Lin 學習筆記 - All Rights Reserved
Powered by Blogger
Template Design by Cool Blogger Tutorials
Published by Templates Doctor