A Generic Data Mapper for Simple Data

Recently, a buddy and I were working on a project and we came across an almost abnormal amount of simple data entities which were used primarily for populating dropdowns. You know the type of data, there is essentially an ID, Name, and not much else.

We needed to do several things with these simple entities, namely populate dropdown, but also associate the entities with other more complex entities. In our architecture, we’re using a layered approach and manual mappers of data retrieval to business entities. There were several ways that we could have gone about propagating this data through the DAL, BLL, and to the presentation, but we chose to create individual entities to represent each of these lists of data. Our reasoning for this was to optimize the ability to separate the code and provide for future classes that could take on additional responsibility if these entities became more complex.

For these simple entities, we didn’t want to write an individual mapper for each one (essentially a whole lot of cut and paste work); so we opted to create a generic mapper class which is constrained by a base class which all of the simple entities inherit from (SimpleData).

The SimpleData class is just that, simple, and looks like this:

The SimpleData Entity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Common.Entities.General
{
    [DataContract()]
    public abstract class SimpleData
    {
        #region Constructors

        public SimpleData()
        {
        }

        public SimpleData(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public SimpleData(string text, string value) : this(0, text, value)
        {            
        }

        public SimpleData(int id, string text, string value)
        {
            this.ID = id;
            this.DataText = text;
            this.DataValue = value;
        }

        #endregion

        #region Properties

        public DateTime Created { get; set; }
        public string DataText { get; set; }
        public string DataValue { get; set; }
        public int ID { get; set; }
        public DateTime? LastUpdated { get; set; }

        #endregion

        #region Methods

        public abstract SimpleData Create(int id, string text, string value);

        #endregion
    }
}

Now that we had a base class to inherit from, we needed to apply this to our simple entity to be mapped; here is one example of our entity inheriting SimpleData:

The Entity to be Mapped

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common.Entities.General
{
    /// 
    /// An arbitrary class implementing SimpleData.
    /// 
    public class CallType : SimpleData
    {
        #region Constructors

        public CallType()
        {
        }

        public CallType(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public CallType(string text, string value) : base(text, value)
        {            
        }

        public CallType(int id, string text, string value)
            : base(id, text, value)
        {
        }

        #endregion

        public override SimpleData Create(int id, string text, string value)
        {
            return new CallType(id, text, value);
        }
    }
}

Now that we had a consistent approach to marking up the simple entities, we could create a single generic mapper class called “SimpleMapper”. The class takes in a DataRow, the name of the ID field, the name of the textual (Name) field, and the name of the value field (specifically, if this is different than the ID).

The reason we have a few extra input parameters here is so the SimpleMapper could handle for these two scenarios (we’ll use our Call Type as the example):

  • Scenario 1: My data has an integer ID for the unique identifier of the database record and the name of the Call Type as the textual value. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
  • Scenario 2: My data has an integer ID for the unique identifier of the database record, a name of the Call Type as the textual value, and a 3-digit textual code for the CallType. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
Code varchar(3)

By specifying which column should be returned as the DataValue field (either the ID, the code, or even the textual name itself) the class has a little more flexibility. Below is the SimpleMapper class in its entirety:

The SimpleMapper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Entities.General;
using System.Data;

namespace DataAccess.Full
{
    public class SimpleMapper where T : SimpleData, new()
    {
        public static T Map(DataRow row, string idName, string textName, string valueName)
        {
            var creator = new T();
            int id = (int)row[idName];
            string text = (string)row[textName];

            string value = id.ToString();
            if (idName != valueName)
            {
                value = (string)row[valueName];
            }

            var obj = creator.Create(id, text, value);
            
            return (T)obj;
        }

        public static T[] Map(DataTable data, string idName, string textName, string valueName)
        {
            var entities = new List();
            foreach (DataRow row in data.Rows)
            {
                entities.Add(Map(row, idName, textName, valueName));
            }

            return entities.ToArray();
        }
    }
}

2 thoughts on “A Generic Data Mapper for Simple Data

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s