передача объекта подключения БД к методам
-
05-07-2019 - |
Вопрос
Интересно, рекомендуется ли передавать объект подключения к базе данных (другим модулям) или позволить методу (в другом модуле) позаботиться о его настройке. Я склоняюсь к тому, чтобы позволить методу настроить его так, чтобы не нужно было проверять состояние соединения перед его использованием, и просто чтобы вызывающая сторона передавала все необходимые данные вызывающему методу, которые были бы необходимы для установки соединения. Р>
Решение
Лично мне нравится использовать узко ограниченные соединения; откройте их поздно, используйте их и закройте (в блоке «using», все в локальном методе). В большинстве случаев пул соединений будет иметь дело с повторным использованием соединения, поэтому в этом подходе нет никаких дополнительных затрат.
Главное преимущество в передаче соединений раньше заключалось в том, чтобы вы могли передавать транзакцию; однако TransactionScope
представляет собой более простой способ обмена транзакциями между методами . р>
Поскольку классы зависят от реализации, я бы написал каждый для открытия своей собственной собственной транзакции. В противном случае вы можете использовать фабричные методы ado.net для создания соответствующего типа из файла конфигурации (имя провайдера).
Другие советы
Лично мне нравится хранить стек моего текущего открытого соединения и транзакций поверх Локальное хранилище потоков с использованием SetData и GetData. Я определяю класс, который управляет моими соединениями с базой данных и позволяет ему использовать шаблон dispose. Это избавляет меня от необходимости передавать соединения и транзакции, что, как мне кажется, мешает и усложняет код. Р>
Я бы настоятельно рекомендовал против оставить методы открытия соединений каждый раз, когда им нужны данные. Это приведет к действительно плохой ситуации, когда сложно управлять транзакциями во всем приложении, и слишком много соединений открываются и закрываются (я знаю о пуле соединений, поиск соединения из пула по-прежнему обходится дороже, чем повторно использовать объект)
Так что в итоге я получил что-то вроде этого (полностью не проверено):
class DatabaseContext : IDisposable {
List<DatabaseContext> currentContexts;
SqlConnection connection;
bool first = false;
DatabaseContext (List<DatabaseContext> contexts)
{
currentContexts = contexts;
if (contexts.Count == 0)
{
connection = new SqlConnection(); // fill in info
connection.Open();
first = true;
}
else
{
connection = contexts.First().connection;
}
contexts.Add(this);
}
static List<DatabaseContext> DatabaseContexts {
get
{
var contexts = CallContext.GetData("contexts") as List<DatabaseContext>;
if (contexts == null)
{
contexts = new List<DatabaseContext>();
CallContext.SetData("contexts", contexts);
}
return contexts;
}
}
public static DatabaseContext GetOpenConnection()
{
return new DatabaseContext(DatabaseContexts);
}
public SqlCommand CreateCommand(string sql)
{
var cmd = new SqlCommand(sql);
cmd.Connection = connection;
return cmd;
}
public void Dispose()
{
if (first)
{
connection.Close();
}
currentContexts.Remove(this);
}
}
void Test()
{
// connection is opened here
using (var ctx = DatabaseContext.GetOpenConnection())
{
using (var cmd = ctx.CreateCommand("select 1"))
{
cmd.ExecuteNonQuery();
}
Test2();
}
// closed after dispose
}
void Test2()
{
// reuse existing connection
using (var ctx = DatabaseContext.GetOpenConnection())
{
using (var cmd = ctx.CreateCommand("select 2"))
{
cmd.ExecuteNonQuery();
}
}
// leaves connection open
}
Для целей автоматического тестирования его обычно легче передать. Это называется внедрение зависимостей . р>
Когда вам нужно написать тесты, вы можете создать фиктивный объект подключения к базе данных и передать его вместо реального. Таким образом, ваши автоматические тесты не будут полагаться на реальную базу данных, которую нужно каждый раз заполнять данными.
Я лично работаю, чтобы максимально централизовать доступ к данным, однако, если это невозможно, я ВСЕГДА открываю новое соединение в других классах, так как я обнаружил, что при передаче слишком много других препятствий. фактический объект подключения.
Вот немного больше понимания этой проблемы. У меня есть класс, который управляет соединениями БД, и у меня есть 2 класса, которые реализуют интерфейс. Один из классов предназначен для SQL, а другой - для OLAP. Менеджер - это тот, кто знает, какое соединение использовать, поэтому он может передать точное соединение типу или тип может создать свое собственное соединение.
Вы можете передавать объекты соединения без каких-либо проблем (например, Microsoft Enterprise Library позволяет передавать вызовы статических методов в соединении), или вы можете управлять им внешне в соответствии с вашим дизайном, прямых технических компромиссов нет.
Будьте осторожны, чтобы переносимость не передавала конкретное соединение, если ваше решение будет перенесено в другие базы данных (т. е. не передавайте SqlConnection, если вы планируете работать с другими базами данных)
Установка соединения является потенциально дорогостоящей и потенциально добавляет туда и обратно. Итак, опять же, потенциально, лучший дизайн - передать объект подключения.
Я говорю потенциально, потому что, если вы являетесь приложением Microsoft ADO, вы, вероятно, используете пул соединений ....
Я бы посоветовал вам различать объект соединения и его состояние (открыто, закрыто). Р>
У вас может быть один метод (или свойство), который читает строку подключения из web.config. Использование одной и той же версии строки подключения каждый раз гарантирует, что вы выиграете от пула подключений.
Вызывайте этот метод, когда вам нужно открыть соединение. В самый последний момент, после настройки всех свойств SqlCommand, откройте соединение, используйте его, а затем закройте. В C # вы можете использовать оператор using, чтобы убедиться, что соединение закрыто. Если нет, обязательно закройте соединение в блоке finally.
Я бы использовал web.config
<configuration>
<connectionStrings>
<add name="conn1" providerName="System.Data.SqlClient" connectionString="string here" />
<add name="conn2" providerName="System.Data.SqlClient" connectionString="string here" />
</connectionStrings>
</configuration>
Затем вы можете ссылаться на него из любого места в приложении