Preparing for Dapper (Part 3)

The team decided that they would create POCOs to represent data models. It is the first step to making applications data source agnostic. The second step is to actually define the objects that would shield applications from the data source.

Working with databases means you'll need a way to connect to them first before you can actually start accessing anything. In the same tradition that ORMs started, I created, well for the lack of a better name, the "database context". Unlike EF's DbContext, which is a true unit of work implementation, my database context is not UoW at all. My database context's main intent is to centralize the management of the connection and transaction objects.

The database context starts with an interface.

IDbDataContext.cs
using System;
using System.Data;

namespace Mendz.Data
{
    public interface IDbDataContext : IDisposable
    {
        IDbConnection Context { get; }

        IDbTransaction Transaction { get; }

        void CreateContext();

        void BeginTransaction(
            IsolationLevel isolationLevel = 
                IsolationLevel.ReadCommitted);

        void EndTransaction(
            EndTransactionMode mode = 
                EndTransactionMode.Commit);
    }
}
This code defines an interface listing the context and transaction attributes and behaviors for the database context. This interface is intentionally compatible with Dapper, which also uses IDbConnection and IDbTransaction internally. I actually had a specific base implementation in mind, so I took the liberty to create an abstract class for it. But first, let's define the EndTransactionMode enumerator.

EndTransactionMode.cs
namespace Mendz.Data
{
    public enum EndTransactionMode
    {
        Rollback,
        Commit
    }
}
This code enumerates the IDbDataContext.EndTransaction()'s possible modes, which are to either commit or rollback a transaction.

DbDataContextBase.cs
using System.Data;

namespace Mendz.Data
{
    public abstract class DbDataContextBase IDbDataContext
    {
        protected abstract IDbConnection BuildContext();

        #region IDbDataContext Support
        protected IDbConnection _context = null;
        public IDbConnection Context {
            get
            {
                CreateContext();
                return _context;
            }
        }

        protected IDbTransaction _transaction = null;
        public IDbTransaction Transaction
        {
            get
            {
                return _transaction;
            }
        }

        public virtual void CreateContext()
        {
            if (_context == null)
            {
                _context = BuildContext();
                _context.Open();
            }
        }

        public virtual void BeginTransaction(
            IsolationLevel isolationLevel = 
                IsolationLevel.ReadCommitted)
        {
            if (_transaction == null)
            {
                CreateContext();
                _transaction = 
                    _context.BeginTransaction(isolationLevel);
            }
        }

        public virtual void EndTransaction(
            EndTransactionMode mode = 
                EndTransactionMode.Commit)
        {
            if (_transaction != null)
            {
                if (mode == EndTransactionMode.Commit)
                {
                    _transaction.Commit();
                }
                else
                {
                    _transaction.Rollback();
                }
                _transaction.Dispose();
                _transaction = null;
            }
        }
        #endregion

        #region IDisposable Support
        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (_transaction != null)
                    {
                    _transaction.Dispose();
                    }
                    if (_context != null)
                    {
                        _context.Dispose();
                    }
                }
                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
        #endregion
    }
}

This code defines an abstract class DbDataContextBase to serve as the base class for implementing database contexts. An important feature offered by DbDataContextBase is the protected abstract method BuildContext(). Through this, implementers can define their own way of actually "building" the IDbConnection instance, which DbDataContextBase exposes as the Context property. DbDataContextBase also implements the IDisposable pattern, to make sure that the data context can properly dispose its likewise IDisposable Context and Transaction properties.

Comments