Ответ 1
Извините за комментирование в первую очередь, но я публикую почти каждый день аналогичный комментарий, так как многие считают, что было бы разумно инкапсулировать функциональность ADO.NET в DB-Class (мне тоже 10 лет назад), В основном они решают использовать статические/общие объекты, поскольку они, кажется, быстрее, чем создавать новый объект для любого действия.
Это не хорошая идея с точки зрения эффективности, ни с точки зрения безопасности при прорыве.
Не используйте на панели Connection-Pool
Существует хорошая причина, по которой ADO.NET внутренне управляет базовыми Связями с СУБД в ADO-NET Connection-Pool:
На практике большинство приложений используют только один или несколько разных конфигурации для соединений. Это означает, что во время применения выполнение, много идентичных соединений будут повторно открываться и закрыто. Чтобы минимизировать затраты на открытие соединений, ADO.NET использует метод оптимизации, называемый пулом соединений.
Пул соединений уменьшает количество новых подключений должен быть открыт. Пулёт сохраняет право собственности на физическое подключение. Он управляет соединениями, поддерживая живой набор активных соединений для каждой конфигурации соединения. Всякий раз, когда пользователь вызовы Открываются при подключении, пул ищет доступную соединение в пуле. Если объединенное соединение доступно, оно возвращает его вызывающему абоненту вместо открытия нового соединения. Когда вызовы приложений Закрыть соединение, пул вернет его в объединенный набор активных соединений вместо его закрытия. Однажды соединение возвращается в пул, оно готово к повторному использованию на next Открыть вызов.
Таким образом, очевидно, что нет причин не создавать, открывать или закрывать соединения, так как на самом деле они не созданы, открыты и закрыты вообще. Это "только" флаг для пула соединений, который должен знать, когда соединение можно использовать повторно. Но это очень важный флаг, потому что, если соединение "используется" (пул соединений предполагает), новое физическое соединение должно быть открыто для СУБД, что очень дорого.
Итак, вы не улучшаете производительность, а наоборот. Если достигнут максимальный размер пула (100 по умолчанию), вы даже получите исключения (слишком много открытых подключений...). Таким образом, это не только сильно повлияет на производительность, но также станет источником неприятных ошибок и (без использования транзакций) области демпинга данных.
Если вы даже используете статические подключения, вы создаете блокировку для каждого потока, пытающегося получить доступ к этому объекту. ASP.NET представляет собой многопоточную среду по своей природе. Поэтому у них отличный шанс для этих замков, что в лучшем случае вызывает проблемы с производительностью. На самом деле рано или поздно вы получите много разных исключений (например, ExecuteReader требует открытого и доступного соединения).
Заключение:
- Не используйте повторно соединения или любые объекты ADO.NET.
- Не делайте их static/shared (в VB.NET)
- Всегда создавайте, открывайте (в случае подключений), используйте, закрывайте и удаляйте их там, где они вам нужны (например, в методе).
- используйте
using-statement
для размещения и закрытия (в случае подключений) неявно
Это верно не только для 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 [email protected]";
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;
}