IDisposableデータベースリソースを効率的に使用するためにC#コンソールアプリケーションを構成するにはどうすればよいですか?
-
06-07-2019 - |
質問
これは、C#コンソールアプリケーション用に提案された(問題空間を説明するために非常に簡略化された)設計です。データベース接続はIDisposableを実装しますが、このソリューションではデータベース接続オブジェクトを using
することはできません。誰かがコンソールアプリケーションのより正しい構造を提案できますか?これは頻繁に解決する必要がある問題です。
class Program
{
SQLiteConnection sourceConnection;
SQLiteConnection destinationConnection;
static void Main(string[] args)
{
Program shell = new Program();
// get connection strings from command line arguments
string sourceConnectionString = shell.getConnectionString(args);
string destinationConnectionString = shell.getConnectionString(args);
// call non-static methods that use
shell.setUpConnections(sourceConnectionString, destinationConnectionString);
shell.doDatabaseWork();
}
private void setUpConnections(string sourceConnectionString, string destinationConnectionString)
{
sourceConnection = new SQLiteConnection(sourceConnectionString);
destinationConnection = new SQLiteConnection(destinationConnectionString);
}
private void doDatabaseWork()
{
// use the connections here
}
}
編集:
一部の人々は、なぜそれらをメンバー変数として必要とするのか理解できません。これは、doDatabaseWorkでの使用のユースケースです(少しコード化されています):
foreach (Row sourceRow in DBResultSet)
{
string sourceXml = sourceRow.Columns["MyColumnName"].Value;
string destinationXML = transformUsingXSLT(sourceXml);
writeToDestination(destinationXml);
}
このループの存続期間中、これらの接続を開いたままにしておきたい方法を参照してください
解決
最善の解決策は、プログラムクラスからメインロジックを抽出することだと思います。 Programクラスは、主要な作業の何らかのスターターです。また、SqlConnectionsのラッパーを提供するのは良いアイデアではありません。これらは既に管理されているリソースであるため、ラップするのは冗長です。したがって、私のソリューションは次のようになります。
class ProgramCore : IDisposable
{
internal ProgramCore(string sourceConnectionString, string destinationConnectionString)
{
setUpConnections(sourceConnectionString, destinationConnectionString);
}
internal void Execute()
{
// do whatever you want
doDatabaseWork();
// do whatever you want
}
public void Dispose()
{
if (_sourceConnection != null)
_sourceConnection.Dispose();
if (_destinationConnection != null)
_destinationConnection.Dispose();
}
private void setUpConnections(string sourceConnectionString, string destinationConnectionString)
{
_sourceConnection = new SQLiteConnection(sourceConnectionString);
_destinationConnection = new SQLiteConnection(destinationConnectionString);
}
private void doDatabaseWork()
{
// use the connections here
}
private SQLiteConnection _sourceConnection;
private SQLiteConnection _destinationConnection;
}
class Program
{
static void Main(string[] args)
{
// get connection strings from command line arguments
string sourceConnectionString = GetConnectionString(args);
string destinationConnectionString = GetConnectionString(args);
using (ProgramCore core = new ProgramCore(sourceConnectionString, destinationConnectionString))
{
core.Execute();
}
}
static string GetConnectionString(string[] args)
{
// provide parsing here
}
}
他のヒント
IDisposableを実装するクラスの記述はどうですか。
クラスコンストラクター内で、DB接続をインスタンス化できます。
次に、IDisposable.Disposeメソッド内で、DB接続を閉じるための分解コードを記述します。
これは、私が何を意味するかを示すためのコードサンプルです。
public class DBWrapper : IDisposable
{
public SqlConnection Connection1 { get; set; }
public SqlConnection Connection2 { get; set; }
public DBWrapper()
{
Connection1 = new SqlConnection();
Connection1.Open();
Connection2 = new SqlConnection();
Connection2.Open();
}
public void DoWork()
{
// Make your DB Calls here
}
public void Dispose()
{
if (Connection1 != null)
{
Connection1.Dispose();
}
if (Connection2 != null)
{
Connection2.Dispose();
}
}
}
そして、プログラムクラスのメインメソッド内から:
class Program
{
static void Main(string[] args)
{
using (DBWrapper wrapper = new DBWrapper())
{
wrapper.DoWork();
}
}
}
スコットの答えはそれを行う一つの方法です。代わりにtry {}を使用することも検討できますか?
static void Main(string[] args)
{
Program shell = new Program();
// get connection strings from command line arguments
string sourceConnectionString = shell.getConnectionString(args);
string destinationConnectionString = shell.getConnectionString(args);
// call non-static methods that use
shell.setUpConnections(sourceConnectionString, destinationConnectionString);
try
{
shell.doDatabaseWork();
}
finally
{
if(sourceConnection != null)
sourceConnection.Dispose();
if(destinationConnection != null)
destinationConnection.Dispose();
}
}
個人的には、あなたはこれを考えすぎていると思います。このスレッドのコードサンプルは非常に複雑です。 IDisposableが終了時に破棄されるため、なぜ人々がプログラムクラスにIDisposableを実装しているのか、私にはわかりません。
使用しない理由や、using(){}ステートメントを使用できない理由は考えられません。
接続を開いて保持しますか?どうして?実際の接続はすべて、.net接続プーリングの背後にあるため、Connectionオブジェクトの新規作成は大したことではありません。必要に応じて開いたり閉じたりするだけで、接続プーリングがすべてを裏で処理します。
サンプルを編集してクラスにラップし、カプセル化もできるようにしました。
class Program
{
static void Main(string[] args)
{
DBWorker worker = new DBWorker();
worker.DoDatabaseWork();
}
}
public class DBWorker
{
private void DoDatabaseWork()
{
using (SQLiteConnection sourceDB = new SQLiteConnection( GetConnectionString() ))
{
sourceDB.Open();
using (SQLiteConnection destDB = new SQLiteConnection( GetConnectionString() ))
{
destDB.Open();
}
}
}
}
うーん、私は誰もこの方法でそれについて言及していないようです。 using
で使用される変数をローカルで宣言する必要はありません。
class Program
{
SQLiteConnection sourceConnection;
SQLiteConnection destinationConnection;
static void Main(string[] args)
{
Program shell = new Program();
// get connection strings from command line arguments
string sourceConnectionString = shell.getConnectionString(args);
string destinationConnectionString = shell.getConnectionString(args);
using (sourceConnection = new SQLiteConnection(sourceConnectionString))
using (destinationConnection = new SQLiteConnection(destinationConnectionString))
{
shell.doDatabaseWork();
}
}
private void doDatabaseWork()
{
// use the connections here
}
}