Извините, что только комментирую в первую очередь, но я публикую почти каждый день аналогичный комментарий, поскольку многие люди думают, что было бы разумно инкапсулировать функциональность ADO.NET в DB-Class (я тоже 10 лет назад). В основном они решают использовать статические / общие объекты, поскольку это кажется быстрее, чем создание нового объекта для любого действия.
Это не лучшая идея ни с точки зрения производительности, ни с точки зрения отказоустойчивости.
Не переманивайте на территории пула подключений
Есть веская причина, по которой ADO.NET внутренне управляет базовыми соединениями с СУБД в пуле соединений ADO-NET :
На практике большинство приложений используют только одну или несколько различных конфигураций для соединений. Это означает, что во время выполнения приложения многие идентичные соединения будут многократно открываться и закрываться. Чтобы минимизировать затраты на открытие соединений, ADO.NET использует метод оптимизации, называемый пулом соединений.
Пул соединений сокращает количество открытий новых соединений. Группа пула сохраняет владение физическим подключением. Он управляет подключениями, поддерживая в рабочем состоянии набор активных подключений для каждой заданной конфигурации подключения. Всякий раз, когда пользователь вызывает Open для соединения, пулер ищет доступное соединение в пуле. Если объединенное соединение доступно, оно возвращает его вызывающей стороне вместо открытия нового соединения. Когда приложение вызывает Close для соединения, пулер возвращает его в объединенный набор активных соединений вместо того, чтобы закрывать его. Как только соединение возвращается в пул, оно готово к повторному использованию при следующем вызове Open.
Итак, очевидно, что нет причин избегать создания, открытия или закрытия соединений, поскольку на самом деле они вообще не создаются, не открываются и не закрываются. Это «всего лишь» флаг для пула соединений, который определяет, можно ли повторно использовать соединение. Но это очень важный флаг, потому что, если соединение «используется» (предполагает пул соединений), новое физическое соединение должно быть открытым для СУБД, что очень дорого.
Таким образом, вы не получаете улучшения производительности, а наоборот. Если будет достигнут указанный максимальный размер пула (по умолчанию 100), вы даже получите исключения (слишком много открытых подключений ...). Таким образом, это не только сильно повлияет на производительность, но и станет источником неприятных ошибок и (без использования транзакций) области сброса данных.
Если вы даже используете статические соединения, вы создаете блокировку для каждого потока, пытающегося получить доступ к этому объекту. ASP.NET по своей природе является многопоточной средой. Так что есть отличная возможность для этих блокировок, которые в лучшем случае вызывают проблемы с производительностью. На самом деле рано или поздно вы получите много разных исключений (например, вашему ExecuteReader требуется открытое и доступное соединение ).
Вывод :
- Ни в коем случае не используйте повторно соединения или какие-либо объекты ADO.NET.
- Не делайте их статическими / общими (в VB.NET)
- Всегда создавайте, открывайте (в случае подключений), используйте, закрывайте и удаляйте их там, где они вам нужны (например, в методе)
- используйте
using-statement
для удаления и закрытия (в случае Connections) неявно
Это верно не только для Connections (хотя это наиболее заметно). Каждый реализующий объект IDisposable
должен быть удален (проще всего using-statement
), тем более в System.Data.SqlClient
пространстве имен.
Все вышесказанное говорит против специального класса DB, который инкапсулирует и повторно использует все объекты. Вот почему я решил выбросить его в корзину. Это только источник проблемы.
Изменить : вот возможная реализация вашего retrievePromotion
метода:
public Promotion retrievePromotion(int promotionID)
{
Promotion promo = null;
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE PromotionID=@PromotionID";
using (var da = new SqlDataAdapter(queryString, connection))
{
// you could also use a SqlDataReader instead
// note that a DataTable does not need to be disposed since it does not implement IDisposable
var tblPromotion = new DataTable();
// avoid SQL-Injection
da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
try
{
connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise
da.Fill(tblPromotion);
if (tblPromotion.Rows.Count != 0)
{
var promoRow = tblPromotion.Rows[0];
promo = new Promotion()
{
promotionID = promotionID,
promotionTitle = promoRow.Field<String>("PromotionTitle"),
promotionUrl = promoRow.Field<String>("PromotionURL")
};
}
}
catch (Exception ex)
{
// log this exception or throw it up the StackTrace
// we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
throw;
}
}
}
return promo;
}