我是否适当地在C#函数中关闭此SQL连接?

时间:2010-06-23 10:54:52

标签: c# sql-server garbage-collection database-connection using-statement

试图关闭我的question on connections remaining open and exceeding the maximum pool,我正在尝试重写用于连接数据库的函数。

该函数存在于本地编译库中。使用反射器我可以看到代码如下所示:

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然后使用'in class,struct或interface member declaration'错误显示“无效标记”。

最好的方法是什么?

更新 我试过拆解调整和重新编译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;
    }
}

我即将调查企业库,似乎可能是前进的方向。

4 个答案:

答案 0 :(得分:4)

建议不要长时间保持连接(请参阅Microsoft Press的Improving .NET Application Performance and Scalability: Patterns and Practices第14章)。

在实践中,我会改变你的课程,而不是让SqlConnection(或SqlDataAdapterSqlCommandBuilder)成为课程的成员(如果必须,那么你应该实现IDisposable模式),而是创建新的实例,包含在使用需要使用它们的类方法的语句中。

答案 1 :(得分:2)

我认为你没有正确地做到这一点。一旦到达using块的末尾,SqlConnection变量就会变得无法使用。如果你想在构造函数之外使用它,不要在SqlConnection变量周围放置using {}(sqlcommand变量MyCommand在构造函数之外间接使用它)。
相反,让你的SqlProvider类实现IDisposable,并在MyCommand,MyDataAdapter,MyDataSet等变量上调用Dispose。
你可能应该在你的SqlProvider类中有这样的东西:

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

如果要在using块中使用它,您的类需要实现IDisposable接口。有关dispose()和IDisposable的指南,请参阅http://msdn.microsoft.com/en-us/library/system.idisposable.dispose%28v=VS.100%29.aspx

答案 2 :(得分:1)

你很亲密。但是,有几个问题。

首先,看起来整个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类

我在企业库方面取得了巨大成功。这是快速,高效的,当我走这条路线时,我从未遇到内存泄漏或数据库连接问题。

答案 3 :(得分:0)

无需将变量设置为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"];