Friday, October 17, 2008

DataMapper implementation in C# using Enterprise Library Data Access Block

Note: I have added an update post on this article in my blog...

Data Access Layer in C# using Repository and Data Mapper


According to Fowler DataMapper is a layer of software that separates the in-memory objects from the database. Its responsibility is to transfer data between the two and also to isolate them from each other. Creating a DataMapper will help you eliminate most of the cross concerns and problems in your code.
In this article I will show how to create a simple DataMapper using the Template pattern and Generics in C#. I am using Enterprise Library 3.1 Data Access Block in this example.
I have my Users Table in the database as follows.
Column Name
Data type
UserID
int
FirstName
varchar(50)
LastName
varchar(50)
Address
varchar(250)
I will create an example to query the user details from this table by using a Template Pattern implementation of DataMapper
I have my DataMapper class which serves are the base class for any DataMapper implemented using Generics as following.
internal abstract class DataMapper
{
protected abstract T EntityMap(IDataRecord record);
public Collection EntityCollectionMap(IDataReader reader)
{
Collection dataCollection = new Collection();
while (reader.Read())
{
dataCollection.Add(EntityMap(reader));
}
return dataCollection;
}
}
The step to follow is to create a Mapper class for my User class.
internal class UserDataMapper : DataMapper<User>
{
protected override User EntityMap(IDataRecord record)
{
User userEntity = new User();
userEntity.Id = (DBNull.Value == record["UserID"]) ? 0 : (int)record["UserID"];
userEntity.FirstName = (DBNull.Value == record["FirstName"]) ? string.Empty : (string)record["FirstName"];
userEntity.LastName = (DBNull.Value == record["LastName"]) ? string.Empty : (string)record["LastName"];
userEntity.Address = (DBNull.Value == record["Address"]) ? string.Empty : (string)record["Address"];
return userEntity;
}
}
To access the data from the database we need to create a Reader class as follows.
internal abstract class EntityReader
{
Database GetDatabase()
{
return DatabaseFactory.CreateDatabase("LocalSqlServerConnection");
}
protected abstract string CommandText { get; }
protected abstract CommandType CommandType { get; }
protected abstract Collection<DbParameter> GetCommandParameters(DbCommand command);
protected abstract DataMapper GetDataMapper();
public Collection GetEntityList()
{
Collection entityList = new Collection();
DbCommand command = (CommandType == CommandType.StoredProcedure) ? GetDatabase().GetStoredProcCommand(CommandText) : GetDatabase().GetSqlStringCommand(CommandText);
foreach (DbParameter commandParameter in GetCommandParameters(command))
command.Parameters.Add(commandParameter);
using (IDataReader reader = GetDatabase().ExecuteReader(command))
{
DataMapper mapper = GetDataMapper();
entityList = mapper.EntityCollectionMap(reader);
return entityList;
}
}
}
The EntityReader class contains an instance of the Enterprise Library Database object that will be used to call the DB operations.
Now to create a Reader class for the User object I need to create a UserReader class as
internal class UserReader : EntityReader<User>
{
protected override string CommandText
{
get
{
return "SELECT USERID, FIRSTNAME, LASTNAME, ADDRESS FROM USERS";
}
}
protected override CommandType CommandType
{
get
{
return CommandType.Text;
}
}
protected override Collection<DbParameter> GetCommandParameters(DbCommand command)
{
Collection<DbParameter> parameters = new Collection<DbParameter>();
switch (command.CommandText)
{
case "GetUsersByName":
DbParameter param = command.CreateParameter();
param.Direction = ParameterDirection.Input;
param.ParameterName = "FirstName";
parameters.Add(param);
break;
}
return parameters;
}
protected override DataMapper<User> GetDataMapper()
{
DataMapper<User> mapper = new UserDataMapper();
return mapper;
}
}
That’s all. I can use the mapper created to query my User collection as
UserReader userReader = new UserReader();
Collection<User> users = userReader.GetEntityList();

No comments: