Pull to refresh

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

Programming *Perfect code *C# *
Предисловие

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

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

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


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

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

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

var collection = db.GetCollection<StrategiesData>();
collection.Save(argObject);


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

<StrategiesData>


Ну, что остается отображение (upd. Оказывается есть и другой вариант — просто продолжать мыслить в рамках обобщенного программирования, но через отображение получается надежнее). Об этом и поговорим.



Отображение обобщенных методов

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

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)

upd.

Мне тут ниже подсказали, оказывается все проще если подумать

public void Save2<T>(T argObject) where T : class
{
  var collection = db.GetCollection<T>();
  collection.Save(argObject);
}
 


Но зато «согрелись» :)

upd 2. И все-таки решение выше без отображения не всегда успешное! Когда происходит работа с иерархией наследования, то тип определяется не верно. Без отображения тип принимается родителя объекта, а не самого объекта. Чуть позже выложу код, чтобы стало понятно.

upd 3.
Более расширенная статья — Отделение логики базы данных (попытка №2)
Tags:
Hubs:
Total votes 18: ↑9 and ↓9 0
Views 3K
Comments Comments 53