4

接続が開いたままであり、最大プールを超えているという私の質問を閉じるために、データベースへの接続に使用される関数を書き直そうとしています。

関数は、自家製のコンパイル済みライブラリ内に存在します。リフレクターを使用すると、コードが次のようになることがわかります。

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 は「クラス、構造体、またはインターフェイス メンバー宣言で無効なトークン 'using' です」というエラーを表示します。

これについて最善の方法は何ですか?

更新 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

4 に答える 4

4

必要以上に長く接続を保持することはお勧めしません (マイクロソフト プレスの.NET アプリケーションのパフォーマンスとスケーラビリティの改善: パターンとプラクティスの第 14 章も参照してください)。

実際には、クラスのメンバーとしてSqlConnection (またはSqlDataAdapterまたは) を持たないようにクラスを変更します(必要な場合は、パターンを実装する必要があります)。代わりに、クラスの using ステートメントでラップされた新しいインスタンスを作成します。それらを使用する必要があるメソッド。SqlCommandBuilderIDisposable

于 2010-06-23T11:14:52.920 に答える
2

私はあなたがそれを正しくやっているとは思わない. using ブロックの最後に達するとすぐに、SqlConnection 変数は unusable になります。コンストラクターの外部で使用する場合は、using {} を SqlConnection 変数の周りに置かないでください (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を参照してください。

于 2010-06-23T11:05:59.167 に答える
1

あなたは近くにいます。ただし、いくつかの問題があります。

まず、DSLibrary全体が何も購入していないようです。

データアクセスを行う場合、通常、接続の取得とコマンドの実行が同じ機能で行われるようにデータアクセスを構成する必要があります。メソッドは、操作の結果のみを返す必要があります。このようにして、接続、コマンド、およびリーダーのIDisposableインターフェースをきれいに使用できます。

次の例では、EnterpriseLibraryを使用しています。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クラスのサブクラス化だけでなく、リーダーをデータテーブルに変換するものがすでにあるはずです。

私はエンタープライズライブラリで大きな成功を収めました。これは高速で効率的であり、このルートを使用するときに、メモリリークやデータベース接続の問題が発生したことはありません。

于 2010-06-23T14:31:46.900 に答える
0

変数を設定する必要はありませんnull- いずれにせよ、それらは GC によって削除されます。

また、 を実装するすべてのクラスに対してDispose()orを呼び出す必要があります。例えば。Close()IDisposableSqlConnection

手動で行うことができます:

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

またはusingブロックを使用して自動的に:

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

(あなたのように)

また、 operator を使用してコードを少し減らすこともできます?:

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

または??:

string str = StrConnection ?? ConfigurationSettings.AppSettings["ConStr"];
于 2010-06-23T11:01:01.847 に答える