using Func<T, TResult> for efficiently using DataReader

Today I was helping on a project which was using ADO.NET SQL Client. Wow, it looks so much scary, and old. I was asked to do some tweaks, so that data layer could be organized. Can I use Repository pattern here, one of the ways to add separation of responsibility these days.

So I started on creating a base class for these repositories. I thought of managing all connection related code in that base class. That was quite easy when I moved ExecuteNonQuery related calls here. Next came the hardest part ExecuteReader, how to send back DataReader without loosing control on my connection state. I can not move all reader related usage in base class, that would negate single responsibility rule.

Hmm, one way would be to create a virtual method in base class, and then each inherited class can override and implement it’s own mapping rules in it. I did small test with that, but soon was struck, what about mapping compositions. If I used this approach then I will have to add multiple conditional cases to map each item properly.

Then I thought of passing in mapper related methods as delegate, not bad. But why not use new concepts, Action<T, T …> / Func<T, …, TResult> / Predicate<T, …, bool>.

I wrote a small application to test this, here is it’s code.

Here is the model I will be using

 
    public class PostItem
    {
        public string Id { get; set; }
        public string Text { get; set; }
        public AuthorItem Author { get; set; }
    }

    public class AuthorItem
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

This is what base class will look

 
    public class RepositoryBase
    {
        protected virtual T ExecuteReader<T>(SqlCommand command, Func<SqlDataReader, T> Mapper)
        {
            using (SqlConnection connection = new SqlConnection())
            {
                try
                {
                    connection.Open();
                    command.Connection = connection;
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        T results = Mapper(reader);
                        return results;
                    }
                }
                finally
                {
                    if (connection.State != ConnectionState.Closed)
                    {
                        connection.Close();
                    }
                }
            }
        }
    }

How I used this to construct my concrete class

 
    public class BlogRepository : RepositoryBase
    {
        public List<PostItem> GetPosts()
        {
            using (SqlCommand command = new SqlCommand())
            {
                command.CommandText = "SELECT * FROM Posts";
                List<PostItem> posts = ExecuteReader<List<PostItem>>(command, MapPosts);

                return posts;
            }
        }

        public PostItem GetPost(string id)
        {
            using (SqlCommand command = new SqlCommand())
            {
                command.CommandText = string.Format("SELECT * FROM Posts WHERE postId = '{0}'", id);
                PostItem post = ExecuteReader<PostItem>(command, MapPost);

                return post;
            }
        }

        public AuthorItem MapAuthor(SqlDataReader reader)
        {
            AuthorItem author = new AuthorItem();

            author.Id = reader.GetString(reader.GetOrdinal("authorId"));
            author.Name = reader.GetString(reader.GetOrdinal("authorName"));

            return author;
        }

        public PostItem MapPost(SqlDataReader reader)
        {
            PostItem post = new PostItem();

            post.Id = reader.GetString(reader.GetOrdinal("postId"));
            post.Text = reader.GetString(reader.GetOrdinal("postText"));

            return post;
        }

        public List<PostItem> MapPosts(SqlDataReader reader)
        {
            List<PostItem> posts = new List<PostItem>();

            while (reader.Read())
            {
                PostItem post = MapPost(reader);

                posts.Add(post);
            }

            return posts;
        }
    }

Look how many mapping options I have using this technique. It looks quite amazing, ha.

One thought on “using Func<T, TResult> for efficiently using DataReader

Leave a Reply