DataReader Wrappers - TypeSafe
Categorized Under: Programming C#
DataReaders (
In the code above, I'm using field names to get at the fields, but it is faster if you index into the fields as shown below.
In both these code snippets there are a few problems.
The class above is designed in such as way as to require only one instance of the class while you're iterating over the DataReader. This technique also has a postivate impact on performance, since you're not creating a new instance of this class for each record in your DataReader.
When you use a DataReader like this (
And the way you'd use this class to return a
DbDataReader
), in .NET give you the fastest access to your data. There is nothing that beats the speed of DataReaders. However, there are a few drawbacks to using them - No Type Checking
- If field ordering or field name changes in your stored procedures or queries will likely break your code
- Code that uses DataReaders tends to be not so clear to read
- ItemId
- MemberId
- ItemTitle
- ItemDesc
- ItemPubdate
- ItemCommentCnt
- ItemAllowComment
using (DbDataReader dr = GetBlog(42)) { var blogs = new List<BlogItem>(); while (dr.Read()) { var blogItem = new BlogItem(); blogItem.ItemId = (long)dr["ItemId"]; blogItem.MemberId = (long)dr["MemberId"]; blogItem.ItemTitle = (string)dr["Itemtitle"]; blogItem.ItemDesc = dr["ItemDesc"] != DBNull.Value ? (string)dr["ItemDesc"] : null; blogItem.ItemPubdate = (DateTime)dr["ItemPubdate"]; blogItem.ItemCommentCnt = (int)dr["ItemCommentCnt"]; blogItem.ItemAllowComment = (bool)dr["ItemAllowComment"]; blogs.Add(blogItem); } return blogs; }
using (DbDataReader dr = GetBlog(42)) { var blogs = new List<BlogItem>(); while (dr.Read()) { var blogItem = new BlogItem(); blogItem.ItemId = (long)dr[0]; blogItem.MemberId = (long)dr[1]; blogItem.ItemTitle = (string)dr[2]; blogItem.ItemDesc = dr[3] != DBNull.Value ? (string)dr[3] : null; blogItem.ItemPubdate = (DateTime)dr[4]; blogItem.ItemCommentCnt = (int)dr[5]; blogItem.ItemAllowComment = (bool)dr[6]; blogs.Add(blogItem); } return blogs; }
- No compile type type checking
- Readability isn't too good
- If a field is null-able (like ItemDesc above) then the code to access the field's value is even more cumbersome
/// <summary> ///This is the Base Class for all DbDataReader Wrappers /// </summary> public class BaseDbDataReaderWrapper { public DbDataReader DbDataReader { get; set; } public BaseDbDataReaderWrapper() { } public BaseDbDataReaderWrapper(DbDataReader dbDataReader) :this() { DbDataReader = dbDataReader; } } /// <summary> ///This class is a wrapper around a DbDataReader, ///Associated with the stored procedure - usp_GET_BLOG_ITEM ///This class provides a strongly typed interface to access data from the DbDataReader. ///containing the result of the given stored procedure. /// </summary> public sealed class BlogItemDrw : BaseDbDataReaderWrapper { public Int64 ItemId { get { return (Int64)DbDataReader[0]; } } public Int64 MemberId { get { return (Int64)DbDataReader[1]; } } public String ItemTitle { get { return (String)DbDataReader[2]; } } public String ItemDesc { get { if (DbDataReader[3] != DBNull.Value) return (String)DbDataReader[3]; else return default(String); } } public DateTime ItemPubdate { get { return (DateTime)DbDataReader[4]; } } public Int32 ItemCommentCnt { get { return (Int32)DbDataReader[5]; } } public Boolean ItemAllowComment { get { return (Boolean)DbDataReader[6]; } } public BlogItemDrw() :base() { } public BlogItemDrw(DbDataReader dbDataReader) :base(dbDataReader) { } }
IEnumerable<T> - DbDataReader
The way you'd use this class is really in a method that returns anIEnumerable<T>
.
public IEnumerable<BlogItem> GetBlogs() { using (DbDataReader dr = GetBlogsDataReader()) { var blogItem = new BlogItem(dr); while (dr.Read()) { yield return blogItem; } } }
IEnumerable<T>
) you have to be careful in that you need to be aware that each time you iterate over the IEnumerable
, you're really querying the database again. Further, the code that returns the DataReader (GetBlogsDataReader()
in this case) should make sure the connection is closed when the DataReader is disposed by using the CommandBehavior.CloseConnection
behavior at the time of calling the ExecuteReader()
method on a command instance like so:
return command.ExecuteReader(CommandBehavior.CloseConnection);
List<T> - DbDataReader
There may be times when you need to use a List<T> instead of an IEnumerable<T> because you either have multiple result sets being returned in the DataReader or you need to fire another query and you can't have a DbDataReader open and fire another query using the same connection. In such situations your class could look like this:public sealed class BlogItem { public Int64 ItemId { get; private set; } public Int64 MemberId { get; private set; } public String ItemTitle { get; private set; } public String ItemDesc { get; private set; } public DateTime ItemPubdate { get; private set; } public DateTime ItemPubtime { get; private set; } public Decimal ItemRatingAvg { get; private set; } public Int32 ItemViewCnt { get; private set; } public Int32 ItemCommentCnt { get; private set; } public Int32 ItemRatingCnt { get; private set; } public Int32 ItemFavoriteCnt { get; private set; } public Boolean ItemIsPrivate { get; private set; } public Boolean ItemAllowComment { get; private set; } public Boolean ItemIsPublished { get; private set; } public BlogItem(DbDataReader dbDataReader) : base() { ItemId = (Int64)dbDataReader[0]; MemberId = (Int64)dbDataReader[1]; ItemTitle = (String)dbDataReader[2]; ItemDesc = dbDataReader[3] != DBNull.Value ? (String)dbDataReader[3] : default(String); ItemPubdate = (DateTime)dbDataReader[4]; ItemRatingAvg = (Decimal)dbDataReader[5]; ItemViewCnt = (Int32)dbDataReader[6]; ItemCommentCnt = (Int32)dbDataReader[7]; ItemRatingCnt = (Int32)dbDataReader[8]; ItemFavoriteCnt = (Int32)dbDataReader[9]; ItemIsPrivate = (Boolean)dbDataReader[10]; ItemAllowComment = (Boolean)dbDataReader[11]; ItemIsPublished = (Boolean)dbDataReader[12]; } }
List<BlogItem>
is
internal List <BlogItem > GetMemberBlogsList(long memberId)
{
using (var dr = DataAccessModule.GetMemberBlogs(memberId))
{
var blogItems = new List <BlogItem >();
while (dr.Read())
blogItems.Add(new BlogItem (dr));
return blogItems;
}
}