Привет всем хабраюзерам, решил написать первую свою статью, которая, продолжает серию очерков про взаимодействие с БД. Так случилось что подвернулся небольшой вэб-проект для реализации. В качестве платформы был выбран ASP.NET MVC + ExtJS но вот решение для ORM сходу не нашлось. Проблема заключалась в том что привлекать большое промышленное ORM решение типа NHibernate или Entity Framework не хотелось, так как проект будет иметь от силы два-три десятка хранимых процедур. Одновременно с этим использовать обвертку от Microsoft DAAB тоже не получается, т.к. в MVC фреймворке модели являются по сути копиями таблиц БД (ну просто для упрощения будем так считать) в результате Reader-ы, DataSet-ы и DataTable-ы нам мало чем могут помочь. Возможно хорошим решением было бы использовать LinqToSql, но мне очень не нравиться когда запросы написаны не на SQL а на C#, я твердо убеждён что общение между приложением и БД должно происходить только посредством хранимых процедур и функций. Другими словами нам нужен маппинг класа на result set хранимой процедуры и простота его использования, что бы просто вызвать хелпер-метод и он, используя рефлексию, вернул коллекцию экземпляров наполненными данными из процедуры. Думаю суть проблемы я изложил достаточно понятно итак приступим к реализации.

Для начала объявим наш класс который будем наполнять данными, само собой, что он будет полностью копировать поля которые возвращает хранимая процедура:
public class Book
{
 public int ID { get; set; }
 public string Title { get; set; }
 public string Author { get; set; }
 public DateTime PublicationDate { get; set; }
}


* This source code was highlighted with Source Code Highlighter.

А вот и хелпер-метод для маппинга:
public static List<T> GetSpResultset<T>(string spName)
{
  List<T> list = new List<T>();
  SqlConnection connection = new SqlConnection("строка соединения");
 
  connection.Open();

  using (SqlCommand command = new SqlCommand(spName, connection))
  {
   command.CommandType = CommandType.StoredProcedure;
   using (IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
   {
    PropertyInfo[] fields = typeof(T).GetProperties();
    while (reader.Read())
    {
      T record = Activator.CreateInstance<T>();
      foreach (PropertyInfo pi in fields)
      {
       if (pi.PropertyType.Name == typeof(Int32).Name)
       {
        if (pi.CanWrite)
        {
         int value = reader.GetInt32(reader.GetOrdinal(pi.Name));
         pi.SetValue(record, value, null);
        }
       }
       else if (pi.PropertyType.Name == typeof(Int16).Name)
       {
        if (pi.CanWrite)
        {
         short value = Convert.ToInt16(reader.GetValue(reader.GetOrdinal(pi.Name)));
         pi.SetValue(record, value, null);
        }
       }
       else if (pi.PropertyType.Name == typeof(String).Name)
       {
        if (pi.CanWrite)
        {
         string value = reader.GetString(reader.GetOrdinal(pi.Name));
         pi.SetValue(record, value, null);
        }
       }
       else if (pi.PropertyType.Name == typeof(DateTime).Name)
       {
        if (pi.CanWrite)
        {
         DateTime value = reader.GetDateTime(reader.GetOrdinal(pi.Name));
         pi.SetValue(record, value, null);
        }
       }
      }
      list.Add(record);
    }
   }
  }
  return list;
}


* This source code was highlighted with Source Code Highlighter.

Метод реализован как generic method который возвращает коллекцию System.Collections.Generic.List&nbsp наполненную экземплярами нашего класса, для простоты реализации в нем опущена передача параметров в процедуру и реализована работа всего с 4 типами данных Int32, Int16, String и DateTime, но вы с легкость сможете его расширить и дополнить функционально.

Ну и конечно же пример использования:
List<Book> books = GetSpResultset<Book>("sp_GetBooks");

* This source code was highlighted with Source Code Highlighter.

Ну вот собственно и все что я хотел рассказать в своей статье, с нетерпением жду замечаний и конструктивной критики.