Well it kind of depends. How many properties are you mapping - if it's only a handful then your current approach is probably best, although it's initially onerous it still makes maintenance easy if the SP result set changes.
Then again if we're talking about many fields coming back from the stored procedure, you could use the very lightweight Linq to SQL - it's officially "dead" but still perfect for this kind of thing and it can map to stored procedure results. Or something even lighter like Rob Conery's Massive which at something like 700 loc is a good option.
And of course Automapper is always an option.
However. If for some reason you can't introduce L2S or any 3rd party packages into the legacy codebase, you could go with a simple reflection based mapper class like the example below.
Given this SQL:
create table MyRecords
(
ID int identity,
Name nvarchar(255),
DateCreated datetime,
IsSilly bit
)
Insert into MyRecords select 'John',getdate(),0
Insert into MyRecords select 'Andrew',getdate(),1
Insert into MyRecords select 'Steve',getdate(),0
Insert into MyRecords select 'Max',getdate(),1
Then this console app shows how simple it really is to generalise this kind of thing; error checking, etc, omitted for brevity, and it doesn't support multiple result sets, but you get the idea of how little code is required.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
namespace MiniORM
{
//Attribute to map SQL result set field to class public instance property
public class FieldInfo : Attribute
{
public string FieldName { get; set; }
public Type DataType { get; set; }
}
public class MyRecord
{
[FieldInfo(DataType=typeof(int),FieldName="ID")]
public int ID { get; set; }
[FieldInfo(DataType = typeof(string), FieldName = "Name")]
public string Name { get; set; }
[FieldInfo(DataType = typeof(DateTime), FieldName = "DateCreated")]
public DateTime DateCreated { get; set; }
[FieldInfo(DataType = typeof(bool), FieldName = "IsSilly")]
public bool IsSilly { get; set; }
public override string ToString()
{
return string.Format("{0}-{1}-{2}-{3}", ID, Name, DateCreated, IsSilly);
}
}
public class FieldInfoDBMapper<T> where T: class,new()
{
private readonly Dictionary<string,KeyValuePair<PropertyInfo,Type>> _mapping;
public FieldInfoDBMapper()
{
var t = typeof (T);
_mapping = new Dictionary<string,KeyValuePair<PropertyInfo,Type>>();
foreach (var pi in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var infos = pi.GetCustomAttributes(typeof(FieldInfo));
if (infos.Any())
{
var fieldInfo = (FieldInfo) infos.First();
_mapping.Add(fieldInfo.FieldName,new KeyValuePair<PropertyInfo, Type>(pi,fieldInfo.DataType));
}
}
}
public List<T> MapFromReader(IDataReader reader)
{
List<T> data = new List<T>();
while (reader.Read())
{
T item = new T();
foreach (var entry in _mapping)
{
var value = Convert.ChangeType(reader[entry.Key],entry.Value.Value);
entry.Value.Key.SetValue(item,value);
}
data.Add(item);
}
return data;
}
}
public class Program
{
static void Main(string[] args)
{
List<MyRecord> records =new List<MyRecord>();
using (
SqlConnection conn =
new SqlConnection("Your connection string here"))
{
conn.Open();
using (SqlCommand comm = new SqlCommand("Select * from MyRecords", conn))
{
var reader = comm.ExecuteReader();
var mapper = new FieldInfoDBMapper<MyRecord>();
records.AddRange(mapper.MapFromReader(reader));
}
}
foreach (var record in records)
{
Console.WriteLine(record.ToString());
}
Console.ReadLine();
}
}
}
Output:
1-John-09/05/2014 00:39:37-False
2-Andrew-09/05/2014 00:39:37-True
3-Steve-09/05/2014 00:39:37-False
4-Max-09/05/2014 00:39:37-True
Hope that helps.