Отделение логики базы данных, а также отображение обобщенных методов

в 18:30, , рубрики: generic object, mongodb, reflection, Программирование, Совершенный код, метки: , , ,

Предисловие

В моем первом посте на Хабре завязался такой диалог:

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

B. «только через хранимые-процедуры» — что же вы NoSQL-продуктам скажете?

Дальше акцент сместился SQL vs. NoSQL. Но были потеряны основы: работу с базой нужно организовать через специально заточенный класс, а не разбрасывать код вызовов по всему проекту.

Я по прежнему считаю, что NoSQL — это слишком молодые продукты, чтобы они могли конкурировать с реляционными базами на полном серьезе. Но у NoSQL и несколько другая ниша. Мне понадобилось некоторое сохранение данных в проекте, где нет больших объемов. И поэтому я решил попробовать MongoDB. (я бы лучше поработал бы с Oracle NoSQL Database, но не нашел как с этим работать на C#).

Ну в общем все достаточно хорошо, чтобы сохранить объект в базе, оказалось надо сделать совсем мало:

var collection = db.GetCollection();
collection.Save(argObject);

где StrategiesData — тип моего объекта, argObject — собственно мой объект. Но такой стиль поощряет раскидывать как раз обращение к базе по всему проекту. Мешает явное указание типа вида . Ну, что остается отображение. Об этом и поговорим.

Отображение обобщенных методов оказалось сделать не так просто. Вот я и решил поделится, может кому пригодится.

public class Database
{
  /// Текущая база данных
  IMongoDatabase db;
 
  public void Save(object argObject)
  {
    MethodInfo MethodGetCollectionGeneric=null;
 
    Type myType = typeof(IMongoDatabase);
    MethodInfo[] myMethod = myType.GetMethods();
    foreach (MethodInfo m in myMethod)
    {
        // Выбираем только методы с названием GetCollection
        if (m.Name == "GetCollection") 
        {
          ParameterInfo[] pi = m.GetParameters();
          // Интересует только такие перегрузки, где возвращаемый тип перегружен, 
          // а метод не имеет параметров
          if (m.ReturnType.IsGenericType && pi.Length==0)
          {
             MethodGetCollectionGeneric = m;
             break;
          }
      }
    }
 
    // Получаем тип нашего объекта
    Type ObjectType = argObject.GetType();
    Type[] typeArgs = { ObjectType };
    // Инстанцируем наш обобщенный метод
    MethodInfo MethodGetCollectionGenericMake = MethodGetCollectionGeneric.MakeGenericMethod(typeArgs);
    // Выполняем метод
    var collection = MethodGetCollectionGenericMake.Invoke(db, null);
    collection.Save(argObject);
}

Итого мы имеем более простое обращение к методу сохранения. И всюду работаем с нашим объектом класса Database. А когда понадобится работать с другими базами, в том числе с реляционными, это легко изменится путем настройки класса Database, а вызов останется тем же. В противном же случае, пришлось бы менять код по всему проекту, что в серьезном проекте аналогично самоубийству. Поэтому никогда не разбрасывайте код обращения к конкретной базе данный по проекту — для этого должен быть выделен специальных класс (или их иерархия, и тогда «менеджер задач» — ваш самый главный класс сверху решает куда сохраняться, выбирая того или иного наследника от вашего Database)

Автор: tac


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js