Как стать автором
Обновить

А Вы как представляете себе Product?

Время на прочтение 5 мин
Количество просмотров 1.2K
Данные, данные, данные… Постоянно приходится с ними работать и, конечно же, хотелось бы иметь для этого максимально комфортные условия.

Предположим есть у нас табличка в базе данных:
Product: id int — первичный ключ, name varchar(256), description text, is_visible bit.

Хотелось бы послушать Ваши мнения, как вы будете работать с данными, которые в ней хранятся. Предположим нужно вывести список продуктов (Уж поскольку я преимущественно работаю с Asp.net) на web-странице.

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

Предположим, что данные из базы мы считали, но нужно их где-то хранить. Определённо сдесь следует использовать некий класс:
  1. public class Product
  2. {
  3.   private Product(int id, string name, string description, bool isActive)
  4.   {
  5.     ID = id;
  6.     Name = name;
  7.     Description = description;
  8.     IsActive = isActive;
  9.   }
  10.  
  11.   int ID { get; private set; }
  12.   string Name { get; set; }
  13.   string Description { get; set; }
  14.   bool IsActive { get; set; }
  15. }

Итак, есть класс, объекты которого будут ассоциироваться с записями в таблице Products, но нету возможности как-либо получить объекты, содержащие реальные данные.

Не проблемма, добавим статический метод, который будет возвращать, к примеру, все записи из таблицы:
  1. public static List<Product> GetAll()
  2. {
  3.   //тут выполняются некоторые действия
  4. }

Так уж получилось, что я предпочёл использвать в качестве коллекции List. Но что делает даный метод?

Вариантов очень много. Самый очевидный — подключается к базе данных, выполняет запрос или хранимую процедуру, а затем помещает данные в коллекцию. Это не очень хороший способ. Вполне возможно, что придётся написать аналогичные методы, получающие только видимые элементы или элементы с непустым описанием.

Для каждого метода будем писать код подключения к базе данных? Конечно нет, просто скопируем, потом не заметим пары нюансов, а потом, думаю вы уже догадались…

Когда 10 месяцев назад я устроился работать программистом, то мне пришлось работать с уже готовым движком интернет магазина. Там методы получения коллекции объектов выглядели примерно так:
  1. public static ProductsCollection GetAll()
  2. {
  3.   //Получаем таблицу
  4.   DataTable table = DBClass.GetTable(query, parameters);
  5.   //Проходим по всей таблице, занося данные в коллекцию
  6.   foreach(DataRow row in table.Rows)
  7.   {
  8.     result.Add(GetProductFromRow(row));
  9.   }
  10.   return result;
  11. }

Сам метод GetAll не подключается в базе данных, это делает метод GetTable, нам остаётся лишь пройтись по таблице, перенося данные в коллекцию.

Удобно, не правдали? К сожалению нет, вернее удобно, но не так удобно, как хотелось бы.

Во-первых, происходит двойное копирование данных (БД => DataTable => Коллекция), во-вторых проход по таблице также придётся писать в каждом аналогичном методе, что является довольно рутиным занятием.

Чего бы хотелось? Примерно следующего:
  1. public static List<Product> GetAll()
  2. {
  3.   return result;
  4. }

И это вполне возможно. Просто нужен универсальный метод получения коллекции объектов.

Поскольку в зависимости от объектов, с которыми этот метод будет работать, результатом будет коллекция именно объектов этого класса, то метод должен быть обобщенным.

Что нужно передавать методу? Во-первых комманду и её тип (sql-запрос или название хп), во-вторых, параметры. Ну и в-третьих, поскольку метод должен знать, как создавать объекты, некий делегат, который будет создавть объект.

Вот что у меня получилось:
  1. // Сообщение о последней возникшей ошибке
  2. public static string Error { get; private set; }
  3. // Строка подключения к базе данных
  4. private static readonly string connectionString = "..."
  5. // Первоначальный размер коллкции при получении нескольких объектов
  6. private const int itemsCount = 30;
  7. //Ну и сам метод
  8. public static List<T> GetList<T>(string commandText, CommandType commandType, DataReaderConstructor<T> dataReaderConstructor, params SqlParameter[] sqlParameters)
  9.   where T : class
  10. {
  11.   try
  12.   {
  13.     using (var connection = new SqlConnection(connectionString))
  14.     {
  15.       var command = new SqlCommand(commandText, connection) { CommandType = commandType };
  16.       if (sqlParameters != null) command.Parameters.AddRange(sqlParameters);
  17.       connection.Open();
  18.       using (var reader = command.ExecuteReader())
  19.       {
  20.         if (reader == null) return null;
  21.         var result = new List<T>(itemsCount);
  22.         while (reader.Read())
  23.         {
  24.           result.Add(dataReaderConstructor(reader));
  25.         }
  26.         return result;
  27.       }
  28.     }
  29.   }
  30.   catch (Exception exeption)
  31.   {
  32.     Error = exeption.Message;
  33.     return null;
  34.   }
  35. }


Всё это находится в неком классе DBClass, за исключением объявления самого делегата, которое находится где-то неподалёку:
  1. public delegate T DataReaderConstructor<T>(IDataReader reader);

Теперь список продуктов можно достаточно просто получить скажем так:
  1. public static List<Product> GetAll()
  2. {
  3.   return DBClass.GetList("SGetProducts", CommandType.StoredProcedure, Constructor)
  4. }

Перед этим объявив в классе Product метод:
  1. private static Product Constructor(IDataReader reader)
  2. {
  3.   return new Product((int)reader[0], (string)reader[1], (string)reader[2], (bool)reader[3]);
  4. }


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

Создать новый класс, где не будет описания по аналогии? Зачем? Ведь у нас есть анонимные типы и мы можем получить коллекцию идентификаторов и названий видимых продуктов используя анонимные типы:
  1. const string query = "Select ID, Name from Product where is_visible";
  2. var products = DBClass.GetList(query, CommandType.Text,
  3.               reader => new
  4.                      {
  5.                        ID = (int) reader[0],
  6.                        Name = (string) reader[1]
  7.                      });
* This source code was highlighted with Source Code Highlighter.


Ну, собственно, вот это мой вариант. Жду критику, советы, предложения, ну и ещё немного критики.
Как Вы предпочитаете работать с данными?
Теги:
Хабы:
-3
Комментарии 14
Комментарии Комментарии 14

Публикации

Истории

Работа

.NET разработчик
66 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн