ExecuteReader требует открытого и доступного соединения. Подключение подключение


при попытке подключения к базе данных MSSQL через ASP.NET в Интернете, я получу следующее, Когда два или более людей подключаются одновременно:

ExecuteReader требует открытого и доступного соединения. Текущее состояние соединения-подключение.

сайт отлично работает на моем localhost сервера.

это грубый код.

public Promotion retrievePromotion()
{
    int promotionID = 0;
    string promotionTitle = "";
    string promotionUrl = "";
    Promotion promotion = null;
    SqlOpenConnection();
    SqlCommand sql = SqlCommandConnection();

    sql.CommandText = "SELECT TOP 1 PromotionID, PromotionTitle, PromotionURL FROM Promotion";

    SqlDataReader dr = sql.ExecuteReader();
    while (dr.Read())
    {
        promotionID = DB2int(dr["PromotionID"]);
        promotionTitle = DB2string(dr["PromotionTitle"]);
        promotionUrl = DB2string(dr["PromotionURL"]);
        promotion = new Promotion(promotionID, promotionTitle, promotionUrl);
    }
    dr.Dispose();
    sql.Dispose();
    CloseConnection();
    return promotion;
}

могу ли я знать, что могло пойти не так и как я могу исправить это?

Edit: не забывайте, что моя строка подключения и соединение находятся в статическом состоянии. Я считаю, что это причина. Посоветуйте, пожалуйста.

public static string conString = ConfigurationManager.ConnectionStrings["dbConnection"].ConnectionString;
public static SqlConnection conn = null;
1 92

1 ответ:

Извините, что только комментирую в первую очередь, но я публикую почти каждый день подобный комментарий, так как многие люди думают, что было бы разумно инкапсулировать ADO.NET функциональность в БД-класс (мне тоже 10 лет назад). В основном они решают использовать статические / общие объекты, так как это кажется быстрее, чем создать новый объект для любого действия.

это не очень хорошая идея ни с точки зрения производительности, ни с точки зрения отказоустойчивости.

не вмешиваться в Соединение-территория бассейна

есть веская причина, почему ADO.NET внутренне управляет базовыми соединениями с СУБД в ADO-NET Connection-Pool:

на практике, большинство приложений используют только один или несколько различных настройки для подключения. Это означает, что во время применения выполнение, много идентичных соединений будут повторно открыты и закрытый. Чтобы минимизировать затраты на открытие соединений, ADO.NET использует метод оптимизации называется пулом соединений.

пул соединений уменьшает количество новых соединений надо открыть. Пулер сохраняет право собственности на физический соединение. Он управляет соединениями, сохраняя в живых набор активных соединения для каждой заданной конфигурации соединения. Всякий раз, когда пользователь вызовы открываются по соединению, пул ищет доступный подключение в пул. Если соединение в пуле доступно, оно возвращает его вызывающий вместо открытия нового соединения. Когда приложение вызывает Close на соединение, пул возвращает его в объединенный набор активных соединений вместо его закрытия. Когда-то соединение возвращается в пул, он готов к повторному использованию на следующий открытый вызов.

поэтому, очевидно,нет причин избегать создания,открытия или закрытия соединений, поскольку на самом деле они не создаются, не открываются и не закрываются вообще. Это" только " флаг для пула соединений с знайте, когда соединение может быть повторно использовано или нет. Но это очень важный флаг, потому что если соединение "используется"(предполагается пул соединений), новое физическое соединение должно быть открыто для СУБД, что очень дорого.

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

если вы даже используете статические соединения, вы создаете блокировку для каждого потока, пытающегося получить доступ к этому объекту. ASP.NET это многопоточная среда по своей природе. Так что есть большой шанс для этих замков, что вызывает проблемы с производительностью в лучшем случае. На самом деле, рано или поздно вы получите много разных исключений(например,ExecuteReader требует открытого и доступного Соединение).

вывод:

  • не используйте повторно соединения или любые ADO.NET объекты вообще.
  • не делайте их статическими / общими(in VB.NET)
  • всегда создавайте, открывайте(в случае соединений), используйте, закрывайте и размещайте их там, где они вам нужны(т. е. в методе)
  • использовать using-statement утилизировать и закрывать(в случае соединений) неявно

это правда не только для соединений (хотя и наиболее заметных). Каждый объект, реализующий IDisposable должны быть утилизированы(простейший,using-statement), тем более в System.Data.SqlClient пространство имен.

все вышесказанное говорит против пользовательского DB-класса, который инкапсулирует и повторно использует все объекты. Вот почему я прокомментировал мусор. Это только источник проблем.


Edit: вот возможная реализация ваших 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;
}