データベース ExecuteReaderについて

タグの編集
投稿者 あさ  (社会人) 投稿日時 2023/8/29 14:57:03
初心者の質問ですみません。
ExecuteReader には Connection が開かれていて使用可能なことが必要です。現在の接続の状態は '接続中" です。
この例外がでて、何が原因か調査中です。
現在、サービスにてDBからデータ取得するSELECTE文の関数があります。
アプリAから関数を呼び出してExecuteReaderで読み出し中(SqlDataReader Close前)に
他のアプリBが同じ関数を呼び出すとエラーになりますか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/8/29 17:01:20
その接続で、既に ExecuteReader が実施されている場合、
その SqlDataReader を閉じるまで、次の操作は行えません。

複数の結果セットを保持したい場合には、このような手法があります。

(案1) 全部読み切ってから閉じて、次のカーソルを開く
 → Dapper .NET などといった ORM ライブラリを併用すると便利
 → WinForms などへの画面表示が必要なら、DataAdapter 経由で DataTable に収める

(案2) SQL Server の MARS (multiple active result-sets) 機構を用いる
https://learn.microsoft.com/ja-jp/previous-versions/sql/sql-server-2008-r2/ms187602%28v=sql.105%29?WT.mc_id=DT-MVP-8907

(案3) トランザクション上の問題が無いようであれば、複数の接続を併用する
投稿者 あさ  (社会人) 投稿日時 2023/8/29 17:04:30

(案3) トランザクション上の問題が無いようであれば、複数の接続を併用する
現状、トランザクション処理は使っていないのですが
案3を行う場合特に何か記述することはないですか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/8/29 17:56:21
SqlDataReader クラスのヘルプから引用。
※機械翻訳が崩れているので、実際には VS2008 付属の人力翻訳版から引用しています。
https://learn.microsoft.com/ja-jp/dotnet/api/system.data.sqlclient.sqldatareader?WT.mc_id=DT-MVP-8907&view=netframework-4.7.2

>> SqlDataReader の使用中は、関連付けられた SqlConnection は、
>> その SqlDataReader によって使用されるためビジー状態になります。
>> この間、SqlConnection に対して、閉じる以外の操作は実行できません。
>> SqlDataReader の Close メソッドを呼び出すまでこの状態が続きます。
>> たとえば、Close を呼び出すまでは、出力パラメータは取得できません。 
>> 
>> データの読み取り中に別のプロセスまたはスレッドが結果セットに加えた変更が、
>> SqlDataReader のユーザーに表示されることがあります。ただし、実際に
>> 表示されるかどうかは、タイミングによって決まります。 
>> 
>> SqlDataReader を閉じた後に呼び出すことができるのは、IsClosed プロパティと
>> RecordsAffected プロパティだけです。RecordsAffected プロパティは、
>> SqlDataReader が存在している間はいつでも呼び出すことできますが、
>> 正しい戻り値を得るために、RecordsAffected の値を取得する前には、
>> 必ず Close を呼び出してください。 


ということで、SQL Server 2005 以降を相手にするのであれば、
先述の案2 にある MARS の利用を検討してみてください。
単一の接続で複数の SqlDataReader を捌くことができます。
https://learn.microsoft.com/ja-jp/dotnet/framework/data/adonet/sql/multiple-active-result-sets-mars?WT.mc_id=DT-MVP-8907


> 現状、トランザクション処理は使っていないのですが
Open → 単一の SqlCommand を処理 → 即時 Close といった処理形態の場合は、
暗黙的なトランザクションが内部的に生成されるので、記述が省略されるケースもしばしばあります。

しかし、複数の SqlCommand を連続して流すような場合は、
明示的に BeginTransaction する運用にしておいた方が良いかと思います。
ストアド内で別の更新処理を呼んでいる場合なども、トランザクション単位を意識してみてください。


複数の接続に対するトランザクションを束ねる場合、TransactionScope という選択肢も
ありますが、サービスが起動していないと呼び出せないので注意しましょう。
https://learn.microsoft.com/ja-jp/dotnet/framework/data/transactions/implementing-an-implicit-transaction-using-transaction-scope?WT.mc_id=DT-MVP-8907
https://www.sukerou.com/2021/06/sql-servertransaction-scope.html