.NetコードでIDbConnectionまたは接続文字列/ファクトリを再共有するベストプラクティス
-
03-07-2019 - |
質問
.Netアプリケーションのデータベースへの接続を維持するための最善の策は何だろうと思っています(ADO.NETですが、どのデータレイヤーでも同じ方法を使用する必要があります)。データベース接続を作成してアプリケーション全体に伝播する必要がありますか、または必要なときに接続文字列/ファクトリを渡してアドホック接続を作成する方が良いでしょう。
パフォーマンスヒットはプーリングでは重要ではないことを理解しているため、破損した接続から簡単に回復できます(新しい接続が作成されるだけです)すべての操作(SQLコマンドではなく、アプリケーション操作)の新しい接続は、追加の重複コードを生成し、時間/リソースの無駄遣いのように感じます(?)。
これら2つのケースについてどう思いますか、それらの短所/長所は何ですか、実際のアプリケーションではどのアプローチを使用していますか
ありがとう
解決
1つのトランザクション内で複数のビジネスオブジェクトがデータベースに自分自身を保存できるように、接続オブジェクトを渡す必要があることに気付きました。
各ビジネスオブジェクトがデータベースへの独自の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ステートメントは接続を閉じます。
これは、Rocky LhotkaのCSLAフレームワークで学んだパターンです。
キース
他のヒント
この問題を自分で処理するべきではありません。あなたのためにそれを行うことができる無数のツールがあります。
本当に自分でやりたい場合は、作業単位パターンを調べてください。接続/トランザクションのライフサイクルを管理できます。さまざまな場所で接続が開かれたり閉じられたりする乱雑な水域をナビゲートしようとするのは確かに望ましくありません。
コンポーネントが直接db接続を開くようにすることにした場合、接続ライフサイクルがきめ細かすぎて、単一のユーザー操作に対して多くのオープン/クローズ接続が発生する可能性があります。
ADO.NET SQL Serverプロバイダーは、接続プーリング自体を実行します。接続文字列の MinPoolSize
および MaxPoolSize
によって、プールサイズを制御できます。
あなたの例で注意すべきことの1つは、ASP.NETアプリはThreadStaticストレージを使用してはならないことです。スレッドを再利用できます。周り。
ASP.NETアプリでは、代わりにHttpContext.Itemsコレクションを使用します。 IDisposableを実装していますが、開発者がDisposeの呼び出しを忘れたり、コードをusingブロックに配置したりするシナリオを見てきました。