[pMixins]

A lightweight open source Mixin and AOP framework for C#


The Repository

As discussed in the Motivation page, implementing the repository pattern was a primary use case for [pMixins]. The key to implementing this pattern was to separate out the logic for each CRUD method.

Key Interfaces
So let's get started. First up is a low level interface for describing entities:

public interface IEntity
{
   int Id {
      get;
   }
}

Next let's define the individual CRUD interfaces.

public interface IReadById<out TEntity> where TEntity : IEntity
{
   TEntity ReadById(int id);
}
public interface IReadAll<out TEntity>
{
   IEnumerable<TEntity> ReadAll();
}
public interface ICreate<in TEntity>
{
   bool Create(TEntity entity);
}
public interface IUpdate<in TEntity>
{
   bool Update(TEntity entity);
}
public interface IDelete<in TEntity>
{
   bool Delete(TEntity entity);
}

Repository Infrastructure
With the interfaces defined, let's setup the base repository infrastructure:

public abstract class RepositoryBase
{
   protected abstract string ConnectionString {
      get;
   }
   private void ExecuteQuery(string cmdTxt, SqlParameter[] sParams, Action<SqlCommand> query)
   {
      //error handling
      //transaction coordinating
      //logging
      using(var sqlConnection = new SqlConnection(ConnectionString))
         using(var sqlCommand = new SqlCommand(cmdTxt, sqlConnection)) {
            //Initialize sqlCommand
            sqlCommand.CommandType = CommandType.StoredProcedure;
            foreach(var p in sParams)
               sqlCommand.Parameters.Add(p);
            //Execute Query
            query(sqlCommand);
         }
   }
   protected void ExecuteNonQuery(string spName, SqlParameter[] sParams)
   {
      this.ExecuteQuery(spName, sParams, cmd => cmd.ExecuteNonQuery());
   }
   protected void ExecuteReader(string spName, SqlParameter[] sParams, Action<SqlDataReader> readerCallback)
   {
      this.ExecuteQuery(spName, sParams, cmd => readerCallback(cmd.ExecuteReader()));
   }
   protected object ExecuteScalar(string spName, SqlParameter[] sParams)
   {
      object returnValue = null;
      this.ExecuteQuery(spName, sParams, cmd => returnValue = cmd.ExecuteScalar());
      return returnValue;
   }
}

The RepositoryBase sets up the abstractions around SqlCommands that Mixins will use later on. Most significantly is the ExecuteQuery method. This is the one method that can talk to the database and provides a single point for talking to the database and wrapping in logging, exception handling and any other non functional logic.

With the RepositoryBase let's setup the first CRUD specific Mixin

public abstract class SqlReadByIdMixin<TEntity> : RepositoryBase, IReadById<TEntity> where TEntity : IEntity
{
   protected abstract string ReadByIdStoredProcedureName {
      get;
   }
   protected abstract TEntity MapEntity(SqlDataReader reader);
   public virtual TEntity ReadById(int id)
   {
      TEntity returnValue = default(TEntity);
      base.ExecuteReader(ReadByIdStoredProcedureName, new[] {
         new SqlParameter("id", id)
      }, reader => returnValue = MapEntity(reader));
      return returnValue;
   }
}

The SqlReadByIdMixin implements the IReadById CRUD interface and implements the ReadById by calling down to RepositoryBase's ExecuteReader. It exposes two abstract members ReadByIdStoredProcedureName and MapEntity that it needs to fully implement ReadById.

This pattern of implementing a single CRUD interface and then declare the abstract members necessary to implement the interface continues with the other Mixins. For example, SqlCreateMixin:

public abstract class SqlCreateMixin<TEntity> : RepositoryBase, ICreate<TEntity> where TEntity : IEntity
{
   protected abstract string CreateStoredProcedureName {
      get;
   }
   protected abstract SqlParameter[] MapEntityToSqlParameters(TEntity entity);
   public virtual bool Create(TEntity entity)
   {
      base.ExecuteNonQuery(CreateStoredProcedureName, MapEntityToSqlParameters(entity));
      return true;
   }
}

I have left out the remaining CRUD Mixins for brevity.

Implementation
Now with the infrastructure setup, lets take a look at an implementation.

First we'll need a simple IEntity:

public class MyEntity : IEntity
{
   public int Id {
      get;
      set;
   }
}

Now that we have MyEntity, let's create a MyEntityRepository

[pMixin(Mixin = typeof(SqlReadByIdMixin<MyEntity>))]
[pMixin(Mixin = typeof(SqlCreateMixin<MyEntity>))]
public partial class MyEntityRepository : IMyEntityRepository
{
   string ISharedRequirements.ConnectionStringImplementation {
      get {
         throw new NotImplementedException();
      }
   }
   MyEntity ISqlReadByIdMixin__MyEntity__Requirements.MapEntityImplementation(SqlDataReader reader)
   {
      throw new NotImplementedException();
   }
   string ISqlReadByIdMixin__MyEntity__Requirements.ReadByIdStoredProcedureNameImplementation {
      get {
         throw new NotImplementedException();
      }
   }
   SqlParameter[] ISqlCreateMixin__MyEntity__Requirements.MapEntityToSqlParametersImplementation(MyEntity entity)
   {
      throw new NotImplementedException();
   }
   string ISqlCreateMixin__MyEntity__Requirements.CreateStoredProcedureNameImplementation {
      get {
         throw new NotImplementedException();
      }
   }
}

MyEntityRepository first and foremost shows the primary goal of [pMixins]: you can control exactly which CRUD are exposed via the Repository!

And looking at the implementation in greater detail, MyEntityRepository explicitly implements the various Requirements interfaces to provide the necessary informations, like stored procedures names and methods for mapping MyEntity objects. Also of interest is that MyEntityRepository implements IMyEntityRepository. This is a simple interface that promotes loose coupling. Consumers of MyEntityRepository should use this interface instead.

The IMyEntityRepository simply aggregates the various CRUD interfaces:

public interface IMyEntityRepository : IReadById<MyEntity>, ICreate<MyEntity>
{
}

Download the source for this and all other recipes Here!