Что такое хороший шаблон проектирования для создания сложных объектов из сложной функции базы данных? - PullRequest
0 голосов
/ 17 мая 2019

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

Я читал о шаблоне проектирования репозитория, но мои операции с базой данных не являются простыми CRUD, и они не просто возвращают простые значения.

Я подключаюсь к PostgreSQL и вызываю функции, которые часто возвращают данные какнабор курсоров.

Вот очень упрощенная часть моего кода ... Я пропустил некоторые детали

class Topic
{
    public string TopicLabel { get; set; }

    public int TopicCode { get; set; }

    List<Topic> parentTopics;

    public Topic(int topicCode , string topicLabel)
    {
        ...
    }

}       


class InitialTopic
{
    Topic initialTopic;

    public int TopicCode { get { return initialTopic.TopicCode; } }            
    Dictionary<int, float> similarityValues;

    public InitialTopic( Topic topic)
    {
        initialTopic = topic;
        similarityValues = new Dictionary<int, float>();
    }

}



class TopicsDictionary
{
    Dictionary<int, Topic> topics;

    public TopicsDictionary()
    {
        topics = new Dictionary<int, Topic>();
    }     

    public Topic this[int topicCode]
    {
        get
        {
            Topic t = null;
            if (topics.ContainsKey(topicCode))
            {
                t = topics[topicCode];                    
            }
            else
            {
                t = new Topic(topicCode);
                topics.Add(topicCode, t);
            }
            return t;
        }
    }
}

     .
     .
     .  

public static void GetData(InitialTopic initialTopic)
{

     using (var conn = new NpgsqlConnection(connString))
    {
        conn.Open();
        NpgsqlTransaction tran = conn.BeginTransaction();

        NpgsqlCommand command = new NpgsqlCommand("public.\"GetStuff\"", conn);
        .
        .

        string cursor1, cursor2;
        using (var dr = command.ExecuteReader())
        {
            dr.Read();
            cursor1 = dr[0].ToString();
            dr.Read();
            cursor2 = dr[0].ToString();
        }                   

        using (var resultSet1 = new NpgsqlCommand())
        {
            resultSet1.CommandText = $@"FETCH ALL FROM ""{cursor1}""";
            resultSet1.Connection = conn;

            using (var reader = resultSet1.ExecuteReader())
            {
                // here read values, create Topic objects,
                // add them to TopicsDictionary and link them using parentTopics list 
                // to reflect parent-child relation
            }
        }           

        using (var resultSet2 = new NpgsqlCommand())
        {
            resultSet2.CommandText = $@"FETCH ALL FROM ""{cursor2}""";
            resultSet2.Connection = conn;

            using (var reader = resultSet2.ExecuteReader())
            {
                // here read values and fill similarityValues 
                // dictionary in InitialTopic object
            }
        }

        tran.Commit();
        conn.Close();

    }
}   

Должен ли я отделять работу базы данных от фактического построения объектов (темаобъекты и их списки участников и словари)?Как мне это сделать?Есть ли подходящий шаблон дизайна для такого случая?

1 Ответ

0 голосов
/ 18 мая 2019

Должен ли я отделять работу базы данных от фактического построения объектов (тематических объектов и их списков членов и словарей)?

Определенно, да.Два ваших блока кода внутри using (var reader = достаточно сложны, так что (1) ваш метод GetData выглядит уродливо, (2) вы хотите выполнить модульные тесты для этого кода, и (3) вы хотите повторно использовать этот код при переключении надругая система баз данных, например, MySQL.

Как мне это сделать?Есть ли подходящий шаблон проектирования для такого случая?

Просто извлеките два кодовых блока из метода GetData куда-нибудь еще.Так как ваш класс InitialTopic довольно чистый, вы можете перейти сюда.Но вам решать.

Теперь единственная проблема заключается в том, как класс InitialTopic получает данные от двух читателей.Конечно, мы передадим читателей объекту initialTopic.Но мы не должны допустить, чтобы класс InitialTopic зависел от класса базы данных (типа ваших читателей).

Обычно для решения этой проблемы мы применяем принцип инверсии зависимости.Введен новый интерфейс для абстрагирования операций читателей:

interface MyReader {
    // hasNext() and next() methods, right?
}

Мы позволим классу InitialTopic зависеть от интерфейса MyReader.

Теперь мы напишем адаптер для адаптацииЧитатели Npgsql (читатели в using (var reader =):

class MyNpgsqlReader : MyReader {
    // use Dependency Injection to inject the Npgsql reader
}

Наконец, два кодовых блока в методе GetData становятся:

using (var reader = resultSet1.ExecuteReader()) {
    initialTopic.method1(new MyNpgsqlReader(reader));
}
...
using (var reader = resultSet2.ExecuteReader()) {
    initialTopic.method2(new MyNpgsqlReader(reader));
}
...