Вопрос

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

Что ж, становится необходимым сохранить эту строку подключения где-нибудь в пределах приложения.Самый распространенный способ, которым я видел, как это делается, - это модуль или глобальный класс с методами get / set для сохранения строки connections.Другой способ, которым я бы сделал это, - использовать Singleton .В любом из вариантов мой DAL может получить доступ к строке подключения, когда это необходимо, с помощью метода GetConnectionString .

Есть ли ЛУЧШИЙ способ сделать это?

Обновить: у меня нет конфигурационного файла, и даже если бы он у меня был, мне понадобилось бы, чтобы строка подключения была прочитана один раз за время существования экземпляра приложения.можете ли вы подробнее остановиться на части "внедрить это в любые классы"

Это было полезно?

Решение

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

Идеальным решением было бы заставить ваше приложение загрузить строку подключения из конфигурации и впрыснуть это в любые классы, которые в этом нуждаются.В зависимости от размера вашего приложения, МоК контейнер, подобный Единство или Виндзорский замок может помочь, но, конечно, не является обязательной частью решения.

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

Обновить: Просто для ясности, забудьте пока все о контейнерах IoC, "Inject" - это просто причудливый способ сказать "передать в качестве параметра" (либо в конструктор класса, либо через свойство, либо что-то еще).

Вместо того, чтобы класс доступа к данным должен запрашивать строку подключения (из какого-то глобального или одноэлементного), передайте ее через конструктор или свойство.

Обновление № 2: Я думаю, что все еще существует некоторое недопонимание относительно того, что влечет за собой такой подход.

В основном это сводится к тому, выглядит ли ваш класс доступа к данным как:

public class DataAccessClass
{
    public DataAccessClass()
    {
        _connString = SomeStaticThing.GetConnectionString();
    }
}

или

public class DataAccessClass
{
    public DataAccessClass(string connString)
    {
        _connString = connString;
    }
}

Эти Статьи (и на самом деле, многие статьи в этом блоге) подробно описывают ряд причин, почему последнее лучше первого (не в последнюю очередь потому, что первое практически невозможно модульно протестировать).

Да, в в каком - нибудь месте в первую очередь должен быть какой-то статический специалист, ответственный за получение строки подключения, но дело в том, что ваши зависимости от статических методов ограничены этим одним местом (которое, вероятно, будет вашим основным методом в процессе начальной загрузки вашего приложения), а не разбросаны по всей вашей кодовой базе.

Другие советы

приятно видеть так много творческих решений такой простой проблемы ;-)

характерные факты, из вопроса OP:

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

итак, нет никакого способа обойти использование статического элемента;является ли это глобальной переменной (на самом деле это сложно сделать в .NET без включающего класса), статическим классом или синглтоном, на самом деле не имеет значения.Решением кратчайшего пути был бы статический класс, инициализированный классом Program, который обрабатывал командную строку.

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

Я не говорю, что вы не хотите дополнить базовое решение чем-то более причудливым, но в этом нет необходимости, и это не устраняет принципиально статический / глобальный характер строки подключения как описано.

Если все, что вы сохраняете, - это строка (возможно, вместе с кучей других глобальных настроек приложения), я бы просто использовал статический класс с кучей свойств для хранения всего этого.Синглтон - это больше работы с кодом и обслуживанием, но в данном случае это ненужная работа, поскольку ваш класс properties ничего не собирается делать;просто держи вещи.

Конечно, есть способ.Во-первых, введите в курс дела внедрение зависимостей и аналогичные методы, а также читайте о уровень обслуживания поскольку это то, что вам здесь нужно.

Что касается сервисного уровня, вам нужна какая-то служба настройки, которая будет модулем, к которому части вашего приложения будут запрашивать информацию о конфигурации - строки подключения, URI и т. Д:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

Должно быть довольно просто, что существует только один экземпляр IConfigurationService в вашей системе, но это достигается с помощью выбранного контейнера DI, а не с помощью одноэлементного шаблона.

Затем все ваши службы DAL получат ссылку на IConfigurationService:

class FooDal : IFooDal
    IConfigurationService ConfigurationService

так что теперь они могут использовать IConfigurationService.ConnectionString чтобы получить доступ к строке подключения.

Это зависит от обстоятельств.

Имейте в виду , что синглтон:

  • обеспечивает, чтобы существовал только один экземпляр класса, и
  • обеспечивает глобальный доступ

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

И последний вариант, конечно, - это использовать локальную переменную.То есть передайте информацию о конфигурации в качестве параметра конструктору или везде, где это необходимо.

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

Есть очень мало случаев, когда вы потребность чтобы гарантировать, что будет существовать ровно один экземпляр.По этой причине глобальный может быть лучшим выбором, чем одноэлементный.

Выбор между локальным и глобальным более сложный.Глобального изменяемого состояния, как правило, лучше избегать. Неизменяемый состояние представляет меньшую проблему и не приведет к тем же проблемам синхронизации / параллелизма / масштабируемости, что и глобальное изменяемое состояние.

Но часто может быть предпочтительнее иметь его в качестве локальной переменной, которую вы передаете компонентам, которым это необходимо.Это можно сделать вручную, просто передав его конструктору объекта, которому требуется доступ к базе данных, или это может быть в некоторой степени автоматизировано с помощью контейнера IoC.

Но в любом случае, если он не является глобальным, то ваш код становится более универсальным и более переносимым.Если у него есть скрытые зависимости от классов и глобальных данных, которые должен существует для того, чтобы код работал, тогда его будет нелегко использовать в других проектах, или если вы слишком сильно рефакторируете всю кодовую базу.

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

как сказал @Anton, вам нужно иметь интерфейс, который предоставляет что-то вроде

interface IConfigurationService
    string ConnectionString

Затем всякий раз, когда одному из ваших классов требуется ваша строка подключения, вы предоставляете ему реализацию IConfigurationService при построении, содержащую допустимую строку.Вам нужно найти подходящее место для создания вашей реализации при запуске вашего приложения (возможно, в то время, когда вы получите адрес своей базы данных) и передать его нуждающемуся в нем классу.

Даже при том, что это может показаться большой работой по сравнению с singleton или global, это снизит сцепление в вашем коде, следовательно, улучшит возможность повторного использования и упростит модульное тестирование, и это довольно просто, как только вы убедите себя, что глобальные переменные (в основном) злые :)

Как упоминалось ранее, есть контейнер IoC, который предоставит основу для этого, но в вашем случае это может быть излишним, плюс приятно использовать шаблон для себя, прежде чем передавать работу в "волшебный ящик"

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top