18

.Netアプリケーションでデータベースへの接続を維持することに関して、何が最善の方法なのか疑問に思っています(ADO.NETですが、どのデータ層でも実践は同じであるはずです)。データベース接続を作成してアプリケーション全体に伝播する必要がありますか、それとも接続文字列/ファクトリを渡して、必要に応じてアドホックに接続を作成する方がよいでしょうか。

私が理解しているように、プーリングではパフォーマンスヒットは重要ではなく、壊れた接続から非常に簡単に回復できます(新しい接続が作成されるだけです)が、接続オブジェクトは優れた比較的高レベルの抽象化であり、新しい接続を作成しますすべての操作 (SQL コマンドではなく、アプリケーション操作) に対して、追加の重複したコードが生成され、時間/リソースの無駄 (?) のように感じます。

これらの 2 つのケースについてどう思いますか?その短所と長所は何ですか?実際のアプリケーションではどのアプローチを使用していますか?

ありがとう

4

4 に答える 4

14

複数のビジネス オブジェクトが単一のトランザクション内でデータベースに保存できるように、接続オブジェクトを渡す必要があることに気付きました。

各ビジネス オブジェクトがデータベースへの独自の SQLConnection を作成する必要がある場合、トランザクションは分散トランザクションにエスカレートするので、それは避けたかったのです。

オブジェクトを保存するために SQLConnection オブジェクトをパラメータとして渡さなければならないのは好きではなかったので、SQLConnection オブジェクトの作成、SQLConnection オブジェクトの使用状況の追跡、および使用されていないときの SQLConnection オブジェクトの切断を処理する ConnectionManager を作成しました。

以下は、ConnectionManager の例としてのコードです。

public class ConnectionManager: IDisposable
{
    private ConnectionManager instance;

    [ThreadStatic]
    private static object lockObject; 
    private static Object LockObject
    {
        get
        {
            if (lockObject == null)
                lockObject = new object();
            return lockObject;
        }
    }

    [ThreadStatic]
    private static Dictionary<string, ConnectionManager> managers;
    private static Dictionary<string, ConnectionManager> Managers
    {
        get
        {
            if (managers == null)
                managers = new Dictionary<string, ConnectionManager>();
            return managers;
        }
    }

    private SqlConnection connection = null;
    private int referenceCount;
    private string name;


    public static ConnectionManager GetManager(string connectionName)
    {
        lock (LockObject)
        {
            ConnectionManager mgr;
            if (Managers.ContainsKey(connectionName))
            {
                mgr = Managers[connectionName];
            }
            else
            {
                mgr = new ConnectionManager(connectionName);
                Managers.Add(connectionName, mgr);
            }

            mgr.AddRef();
            return mgr;
        }
    }

    private ConnectionManager(string connectionName)
    {
        name = connectionName;
        connection = new SqlConnection(GetConnectionString(connectionName));
        connection.Open();
    }

    private string GetConnectionString(string connectionName)
    {
        string conString = Configuration.ConnectionString;
        return conString; 
    }

    public SqlConnection Connection
    {
        get { return connection; }
    }

    private void AddRef()
    {
        referenceCount += 1;
    }

    private void DeRef()
    {
        lock (LockObject)
        {
            referenceCount -= 1;
            if (referenceCount == 0)
            {
                connection.Dispose();
                Managers.Remove(name);
            }
        }
    }

#region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            DeRef();
        }
    }

    ~ConnectionManager()
    {
        Dispose(false);
    }

#endregion

}

ビジネスオブジェクトからそれを使用する方法は次のとおりです。

public void Save()
{   
    using (ConnectionManager mrg = ConnectionManager.GetManager("SQLConnectionString")
    {
        using (SQLCommand cmd = new SQLCommand)
        {
            cmd.connection = mgr.Connection
            // More ADO Code Here
        }

        _childObject.Save(); //this child object follows the same pattern with a using ConnectionManager.
    }
}

ビジネス オブジェクトを保存すると、そのすべての子も同じ接続オブジェクトを使用して保存されます。スコープが元の親から離れると、using ステートメントは接続を閉じます。

これは、CSLA フレームワークで Rocky Lhotka から学んだパターンです。

キース

于 2009-01-05T20:07:42.047 に答える
2

ADO.NET SQL Server プロバイダーは、接続プール自体を行います。プール サイズは、接続文字列で制御できMinPoolSizeますMaxPoolSize

于 2009-01-05T19:32:21.947 に答える
1

この例で注意すべきことの1つは、スレッドを再利用できるため、ASP.NETアプリはThreadStaticストレージを使用しないことです。すべてのオブジェクトをクリーンアップしないと、接続がハングアップすることになります。

ASP.NETアプリでは、代わりにHttpContext.Itemsコレクションを使用します。IDisposableを実装していますが、開発者がDisposeを呼び出すのを忘れたり、コードをusingブロックに配置したりするのを忘れるシナリオを見てきました。

于 2009-12-22T17:27:15.170 に答える
1

この問題を自分で処理できるツールは無数にあるため、自分でこの問題を処理するべきではありません。

本当に自分でやりたい場合は、接続/トランザクションのライフサイクルを管理できるUnit of Workパターンを調べてください。確かに、さまざまな場所で接続が開閉されている乱雑な水域をナビゲートしようとする必要はありません。

コンポーネントが db 接続を直接開くことを決定した場合、接続のライフサイクルが細かすぎて、1 回のユーザー操作で多くの接続が開いたり閉じたりする可能性があります。

于 2009-01-05T19:32:55.457 に答える