Я закрываю это соединение SQL в функции C # соответственно? - PullRequest
4 голосов
/ 23 июня 2010

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

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

public SqlProvider([Optional, DefaultParameterValue("")] string StrConnection)
{
    string str;
    if (StrConnection == "")
    {
        str = ConfigurationSettings.AppSettings["ConStr"];
    }
    else
    {
        str = StrConnection;
    }
    SqlConnection connection = new SqlConnection(str);
    connection.Open();
    this.MyCommand = new SqlCommand();
    SqlCommand myCommand = this.MyCommand;
    myCommand.Connection = connection;
    myCommand.CommandType = CommandType.Text;
    myCommand = null;
    this.MyDataAdapter = new SqlDataAdapter(this.MyCommand);
    this.MyCommandBuilder = new SqlCommandBuilder(this.MyDataAdapter);
    this.MyDataSet = new DataSet();
}

Я планирую изменить это, чтобы прочитать

public SqlProvider([Optional, DefaultParameterValue("")] string StrConnection)
{
    string str;
    if (StrConnection == "")
    {
        str = ConfigurationSettings.AppSettings["ConStr"];
    }
    else
    {
        str = StrConnection;
    }

    using (SqlConnection connection = new SqlConnection(str))
    {
        connection.Open();
        this.MyCommand = new SqlCommand();
        SqlCommand myCommand = this.MyCommand;
        myCommand.Connection = connection;
        myCommand.CommandType = CommandType.Text;
        myCommand = null;
        this.MyDataAdapter = new SqlDataAdapter(this.MyCommand);
        this.MyCommandBuilder = new SqlCommandBuilder(this.MyDataAdapter);
        this.MyDataSet = new DataSet();
    }
}

, а затем перекомпилировать dll.Учитывая, что экземпляр SQLProvider() обычно создается в верхней части открытого класса, а затем этот экземпляр используется внутри членов класса (например:

public class Banner
{
    DSLibrary.DataProviders.SqlProvider db = new DSLibrary.DataProviders.SqlProvider(Defaults.ConnStr);
    public Banner()
    {    
    }

    public DataTable GetBannerImages(string bannerLocation,int DeptId)
    {
        using (DSLibrary.DataProviders.SqlProvider db = new DSLibrary.DataProviders.SqlProvider(Defaults.ConnStr))
        {
            DataTable dt = new DataTable();

            //Add Parameter @BannerLocation for Banner of Specific Location 
            //Call proc_getBannerImages Stored procedure for Banner Images
            db.AddStoredProcParameter("@BannerLocation", SqlDbType.VarChar, ParameterDirection.Input, 100, bannerLocation);
            db.AddStoredProcParameter("@DeptId", SqlDbType.Int, ParameterDirection.Input, 0, DeptId);
            dt = db.ExecuteStoredProcedure("proc_getBannerImages");
            return dt;
        }
    }
}

Правильно ли я это делаю?Мне кажется, что соединение будет удалено до того, как данные будут фактически извлечены. Кроме того, Visual Studio сообщает мне, что SQLProvider() должен быть неявно конвертируемым в System.IDisposable - как мне поступить в реализации этого?

Я попытался обернуть все элементы class Banner в оператор using (DSLibrary.DataProviders.SqlProvider db = new DSLibrary.DataProviders.SqlProvider(Defaults.ConnStr)){}, но intellisense затем отображает ошибку "Недопустимый токен, использующий" в объявлении члена класса, структуры или интерфейса ".

Что является лучшим способомчтобы пойти по этому поводу?

ОБНОВЛЕНИЕ Я пытался разобрать, настроить и перекомпилировать DSLibrary, но, как говорит CHris_Lively, я думаю, что это ничего не делает для меня. Изменение рассматриваемого экземпляра на то, что я получаюболее стандартный формат работает до сих пор:

public DataTable GetBannerImages(string bannerLocation,int DeptId)
{
    using (SqlConnection conn = new SqlConnection(Defaults.ConnStr))
    {
        SqlCommand cmd = new SqlCommand("proc_getBannerImages", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add(new SqlParameter("@BannerLocation", bannerLocation));
        cmd.Parameters.Add(new SqlParameter("@DeptId", DeptId));

        SqlDataAdapter da = new SqlDataAdapter();
        da.SelectCommand = cmd;
        DataTable dt = new DataTable();

        da.Fill(dt);
        return dt;
    }
}

Я собираюсь заглянуть в библиотеку Enterprise, кажется, что это может бытьпуть вперед.

Ответы [ 4 ]

4 голосов
/ 23 июня 2010

Не рекомендуется удерживать подключения дольше, чем требуется (см. Также главу 14 из Повышение производительности и масштабируемости приложений .NET: шаблоны и практики от Microsoft Press).

На практике я бы изменил ваш класс, чтобы вместо него не иметь SqlConnection (или SqlDataAdapter или SqlCommandBuilder) в качестве члена класса (если вы должны , тогда вы должны реализовать шаблон IDisposable), но вместо этого создайте новые экземпляры, заключенные в операторы класса, которые должны их использовать.

2 голосов
/ 23 июня 2010

Я не думаю, что вы делаете это правильно.Как только вы достигнете конца используемого блока, переменная SqlConnection станет непригодной для использования.Если вы хотите использовать его вне конструктора, не помещайте using {} вокруг переменной SqlConnection (переменная sqlcommand MyCommand использует его косвенно вне конструктора).Вместо этого, сделайте так, чтобы класс SqlProvider реализовал IDisposable, и вызовите Dispose для переменных MyCommand, MyDataAdapter, MyDataSet и т. Д.Возможно, в вашем классе SqlProvider должно быть что-то подобное:

    public void Dispose()
    {
        if (MyCommand != null)
        {
            MyCommand.Dispose();
        }
        //... Similarly for MyDataAdapter,MyDataSet etc.
    }

Вашему классу необходимо реализовать интерфейс IDisposable, если вы хотите использовать его в блоке using.См. http://msdn.microsoft.com/en-us/library/system.idisposable.dispose%28v=VS.100%29.aspx для рекомендаций по утилизации () и IDisposable.

1 голос
/ 23 июня 2010

Ты рядом. Тем не менее, пара вопросов.

Во-первых, похоже, что вся DSLibrary вообще ничего вам не покупает.

При доступе к данным вы, как правило, хотите структурировать его так, чтобы получение соединения и выполнение команды выполнялись в одной и той же функции. Ваши методы должны только возвращать результат операции. Таким образом, вы можете без проблем использовать интерфейс IDisposable соединения, команды и считывателя.

В следующем примере используется Enterprise Library . Обратите внимание, что у Db нет предложения using. Он не реализует IDisposable. Вместо этого команда отвечает за освобождение соединения, когда оно выходит из области видимости:

    public static DataTable GetBannerImages(String bannerLocation, Int32 departmentId)
    {
        DataTable result = new DataTable();
        result.Locale = CultureInfo.CurrentCulture;

        Database db = DatabaseFactory.CreateDatabase("NamedConnectionStringFromConfig");

        using (DbCommand dbCommand = db.GetStoredProcCommand("proc_getBannerImages"))
        {
            db.AddInParameter(dbCommand, "BannerLocation", DbType.String, bannerLocation);
            db.AddInParameter(dbCommand, "DeptId", DbType.Int32, departmentId);

            using (IDataReader reader = db.ExecuteReader(dbCommand))
            {
                SopDataAdapter dta = new SopDataAdapter(); // descended from DbDataAdapter

                dta.FillFromReader(result, reader);
            } // using dataReader
        } // using dbCommand

        return result;
    } // method::GetBannerImages

Возможно, у вас уже есть что-то, что преобразует читателя в таблицу данных, если не просто изучить подклассы класса System.Data.Common.DbDataAdapter

Я имел огромный успех в Корпоративной библиотеке. Это быстро, эффективно, и когда я шел по этому пути, у меня никогда не было утечек памяти или проблем с подключением к БД.

0 голосов
/ 23 июня 2010

Нет необходимости устанавливать переменные в null - в любом случае они будут удалены GC.

Также вам необходимо вызвать Dispose() или Close() для всех классов, которые реализуют IDisposable.например, SqlConnection.

Вы можете сделать это вручную:

SqlConnection conn = null;
try
{
    // use conn
}
finally
{
    if (conn != null)
        conn.Close();
}

или автоматически, используя блок using:

using (SqlConnection = new SqlConnection())
{
    // use conn
}

(как вы)

Также вы можете немного уменьшить свой код, используя оператор ?:

string str = String.IsNullOrEmpty(StrConnection) ? ConfigurationSettings.AppSettings["ConStr"] : StrConnection;

или ??:

string str = StrConnection ?? ConfigurationSettings.AppSettings["ConStr"];
...