今更ながらVB.NETからC#への移行メモ
未だ.netに移行出来ないシステムの延命処置をしていますが
VB.NETでADO.NETを使っている状況からC#へのコンバージョンを
行ったところ、トランザクション関連の変更に手間取ったので
こちらにメモしておきます。
TransactionScopeを何故使っていないかというと、あれはクセがあるんですよね
複数ポートの開放等別途OSの設定を弄る必要があったりしたのでそれらを避けたかったのです
こちらは複数のTableAdapterの処理をまとめてコミット、ロールバック出来る
汎用的なクラスになります
作ったのがはるか昔で、C#に移行したら全然動かなかったので各種修正してます。
C#
using System;
using System.Collections.Generic;
using System.Data;
using Microsoft.Data.SqlClient;
using System.Reflection;
namespace goudyDBLib
{
/// <summary>
/// TableAdapterにトランザクション機能を実装するクラス
/// </summary>
/// <remarks>
/// 利用方法の例
/// Dim tat As New TableAdapterTransactor
/// Dim taData1 As New Data1TableAdapter
/// Dim taData2 As New Data2TableAdapter
/// tat.AddTableAdapter(taData1)
/// tat.AddTableAdapter(taData2)
/// tat.BeginTransaction()
/// Try
/// taData1.Insert......
/// taData2.Insert......
/// tat.Commit()
/// Catch ex As Exception
/// tat.Rollback()
/// End Try
/// </remarks>
public class TableAdapterTransactor : IDisposable
{
private SqlConnection _conn = null;
private SqlTransaction _trans = null;
private readonly List<object> _tableAdapters = new List<object>();
/// <summary>
/// 複数の TableAdapter で共通の SqlCommand を取得します。
/// </summary>
/// <returns>共通の SqlCommand オブジェクト</returns>
public SqlCommand JoinSQLCommand()
{
var cmd = new SqlCommand
{
Connection = _conn,
Transaction = _trans
};
return cmd;
}
#region プロシージャ - GetConnection [TableAdapterのConnectionを取得する]
/// <summary>
/// TableAdapterのConnectionを取得する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <returns>取得したConnection</returns>
private SqlConnection GetConnection(object tableAdapter)
{
var type = tableAdapter.GetType();
var connectionProperty = type.GetProperty("Connection", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (connectionProperty != null)
{
return (SqlConnection)connectionProperty.GetValue(tableAdapter, null);
}
throw new InvalidOperationException("Connection プロパティが見つかりませんでした。");
}
#endregion
#region プロシージャ - SetConnection [TableAdapterのConnectionを設定する]
/// <summary>
/// TableAdapterのConnectionを設定する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <param name="connection">Connection</param>
private void SetConnection(object tableAdapter, SqlConnection connection)
{
var adapterType = tableAdapter.GetType();
var connectionProperty = adapterType.GetProperty("Connection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (connectionProperty != null)
{
connectionProperty.SetValue(tableAdapter, connection, null);
return;
}
var connectionField = adapterType.GetField("_connection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (connectionField != null)
{
connectionField.SetValue(tableAdapter, connection);
return;
}
throw new InvalidOperationException("Connection プロパティまたはフィールドが見つかりませんでした。");
}
#endregion
#region プロシージャ - SetAdapter [TableAdapterのDataAdapterを設定する]
/// <summary>
/// TableAdapterのDataAdapterを設定する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <param name="adapter">DataAdapter</param>
/// <summary>
/// TableAdapterのDataAdapterを設定する
/// </summary>
private void SetAdapter(object tableAdapter, SqlDataAdapter adapter)
{
var adapterType = tableAdapter.GetType();
var adapterProperty = adapterType.GetProperty("_adapter", BindingFlags.Instance | BindingFlags.NonPublic);
if (adapterProperty == null)
{
adapterProperty = adapterType.GetProperty("Adapter", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (adapterProperty != null)
{
if (adapterProperty.CanWrite)
{
adapterProperty.SetValue(tableAdapter, adapter, null);
}
else
{
// プロパティが書き込み不可の場合、フィールドを試す
var adapterField = adapterType.GetField("_adapter", BindingFlags.Instance | BindingFlags.NonPublic);
if (adapterField == null)
{
adapterField = adapterType.GetField("Adapter", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (adapterField != null)
{
adapterField.SetValue(tableAdapter, adapter);
}
else
{
throw new InvalidOperationException($"{adapterType.Name} に _adapter または Adapter プロパティおよびフィールドが見つかりませんでした。");
}
}
}
else
{
throw new InvalidOperationException($"{adapterType.Name} に _adapter または Adapter プロパティが見つかりませんでした。");
}
}
#endregion
#region プロシージャ - InitAdapter [TableAdapterのInitAdapterメソッドを呼び出す]
/// <summary>
/// TableAdapterのInitAdapterメソッドを呼び出す
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
private void InitAdapter(object tableAdapter)
{
var adapterType = tableAdapter.GetType();
var initAdapterMethod = adapterType.GetMethod("InitAdapter", BindingFlags.Instance | BindingFlags.NonPublic);
if (initAdapterMethod != null)
{
initAdapterMethod.Invoke(tableAdapter, null);
}
else
{
throw new InvalidOperationException("InitAdapter メソッドが見つかりませんでした。");
}
}
#endregion
#region プロシージャ - InitCommandCollection [TableAdapterのInitCommandCollectionメソッドを呼び出す]
/// <summary>
/// TableAdapterのInitCommandCollectionメソッドを呼び出す
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
private void InitCommandCollection(object tableAdapter)
{
var adapterType = tableAdapter.GetType();
var initCommandCollectionMethod = adapterType.GetMethod("InitCommandCollection", BindingFlags.Instance | BindingFlags.NonPublic);
if (initCommandCollectionMethod != null)
{
initCommandCollectionMethod.Invoke(tableAdapter, null);
}
else
{
throw new InvalidOperationException("InitCommandCollection メソッドが見つかりませんでした。");
}
}
#endregion
#region プロシージャ - SetTransactionCommands [TableAdapterのCommandCollectionのTransactionを設定する]
/// <summary>
/// TableAdapterのCommandCollectionのTransactionを設定する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <param name="transaction">Transaction</param>
private void SetTransactionCommands(object tableAdapter, SqlTransaction transaction)
{
var adapterType = tableAdapter.GetType();
var commandsProperty = adapterType.GetProperty("CommandCollection", BindingFlags.Instance | BindingFlags.NonPublic);
if (commandsProperty != null)
{
SqlCommand[] commands = (SqlCommand[])commandsProperty.GetValue(tableAdapter, null);
foreach (SqlCommand command in commands)
{
command.Transaction = transaction;
}
SetConnection(tableAdapter, transaction.Connection);
}
else
{
throw new InvalidOperationException("CommandCollection プロパティが見つかりませんでした。");
}
}
#endregion
#region プロシージャ - SetTransactionAdapter [TableAdapterのDataAdapterのTransactionを設定する]
/// <summary>
/// TableAdapterのDataAdapterのTransactionを設定する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <param name="transaction">Transaction</param>
private void SetTransactionAdapter(object tableAdapter, SqlTransaction transaction)
{
var adapter = GetAdapter(tableAdapter);
if (adapter.InsertCommand != null)
{
adapter.InsertCommand.Transaction = transaction;
}
if (adapter.DeleteCommand != null)
{
adapter.DeleteCommand.Transaction = transaction;
}
if (adapter.UpdateCommand != null)
{
adapter.UpdateCommand.Transaction = transaction;
}
SetAdapter(tableAdapter, adapter);
// InitCommandCollectionが必要な場合はコメントアウトを解除
// InitCommandCollection(tableAdapter);
}
/// <summary>
/// TableAdapterのDataAdapterを取得する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
/// <returns>取得したDataAdapter</returns>
private SqlDataAdapter GetAdapter(object tableAdapter)
{
var adapterType = tableAdapter.GetType();
var adapterProperty = adapterType.GetProperty("_adapter", BindingFlags.Instance | BindingFlags.NonPublic);
if (adapterProperty == null)
{
// 他のプロパティ名を試す
adapterProperty = adapterType.GetProperty("Adapter", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (adapterProperty != null)
{
return (SqlDataAdapter)adapterProperty.GetValue(tableAdapter, null);
}
throw new InvalidOperationException("_adapter プロパティが見つかりませんでした。");
}
#endregion
#region メソッド - AddTableAdapter [TableAdapterを追加する]
private void ListConnectionMembers(object tableAdapter)
{
var type = tableAdapter.GetType();
var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Console.WriteLine($"Members of {type.Name}:");
foreach (var member in members)
{
Console.WriteLine($"{member.MemberType}: {member.Name}");
}
}
/// <summary>
/// TableAdapterを追加する
/// </summary>
/// <param name="tableAdapter">TableAdapter</param>
public void AddTableAdapter(object tableAdapter)
{
_tableAdapters.Add(tableAdapter);
ListConnectionMembers(tableAdapter);
}
#endregion
#region メソッド - BeginTransaction [トランザクションを開始する]
/// <summary>
/// トランザクションを開始する
/// </summary>
/// <remarks>開始する前に、AddTableAdapterメソッドで対象となるTableAdapterを追加すること</remarks>
public void BeginTransaction()
{
if (_tableAdapters.Count == 0)
throw new InvalidOperationException("トランザクションを開始する前に、少なくとも一つのTableAdapterを追加してください。");
foreach (object adapter in _tableAdapters)
{
if (_conn == null)
{
_conn = GetConnection(adapter);
if (_conn.State != ConnectionState.Open)
{
_conn.Open();
}
_trans = _conn.BeginTransaction();
}
SetConnection(adapter, _conn);
SetTransactionAdapter(adapter, _trans);
SetTransactionCommands(adapter, _trans);
}
}
/// <summary>
/// 既に開始しているトランザクション処理にTableAdapterを参加させる
/// </summary>
/// <param name="tableAdapter">追加するTableAdapter</param>
public void AddTabledapterAfterBeginTransaction(object tableAdapter)
{
if (_conn == null)
throw new InvalidOperationException("トランザクションが開始されていません。");
if (_tableAdapters.Contains(tableAdapter))
return;
SetConnection(tableAdapter, _conn);
SetTransactionAdapter(tableAdapter, _trans);
SetTransactionCommands(tableAdapter, _trans);
AddTableAdapter(tableAdapter);
}
/// <summary>
/// トランザクションを開始する
/// </summary>
/// <param name="conn">コネクション</param>
public void BeginTransaction(SqlConnection conn)
{
if (conn == null)
throw new ArgumentNullException(nameof(conn));
_conn = conn;
if (_conn.State != ConnectionState.Open)
{
_conn.Open();
}
_trans = _conn.BeginTransaction();
foreach (object adapter in _tableAdapters)
{
SetConnection(adapter, _conn);
SetTransactionAdapter(adapter, _trans);
SetTransactionCommands(adapter, _trans);
}
}
/// <summary>
/// トランザクションを開始する
/// </summary>
/// <param name="trans">トランザクション</param>
public void BeginTransaction(SqlTransaction trans)
{
if (trans == null)
throw new ArgumentNullException(nameof(trans));
_conn = trans.Connection;
_trans = trans;
foreach (object adapter in _tableAdapters)
{
SetConnection(adapter, _conn);
SetTransactionAdapter(adapter, _trans);
SetTransactionCommands(adapter, _trans);
}
}
#endregion
#region メソッド - Commit [トランザクションをコミットする]
/// <summary>
/// トランザクションをコミットする
/// </summary>
public void Commit()
{
if (_trans == null)
throw new InvalidOperationException("コミットするトランザクションが存在しません。");
try
{
_trans.Commit();
}
catch (Exception)
{
_trans.Rollback();
throw;
}
finally
{
if (_conn.State == ConnectionState.Open)
{
_conn.Close();
}
_trans.Dispose();
_trans = null;
_conn = null;
}
}
#endregion
#region メソッド - Rollback [トランザクションをロールバックする]
/// <summary>
/// トランザクションをロールバックする
/// </summary>
public void Rollback()
{
if (_trans == null)
throw new InvalidOperationException("ロールバックするトランザクションが存在しません。");
_trans.Rollback();
if (_conn.State == ConnectionState.Open)
{
_conn.Close();
}
_trans.Dispose();
_trans = null;
_conn = null;
}
#endregion
#region メソッド - Dispose [リソースを解放する]
/// <summary>
/// リソースを解放します。
/// </summary>
public void Dispose()
{
if (_trans != null)
{
_trans.Dispose();
_trans = null;
}
if (_conn != null)
{
if (_conn.State == ConnectionState.Open)
{
_conn.Close();
}
_conn.Dispose();
_conn = null;
}
// 他のリソースがあればここで解放
}
#endregion
}
}
VB.NET
Imports System.Data.SqlClient
Imports System.Reflection
''' <summary>
''' TableAdapterにトランザクション機能を実装するクラス
''' </summary>
''' <remarks>
''' 利用方法の例
''' Dim tat As New TableAdapterTransactor
''' Dim taData1 As New Data1TableAdapter
''' Dim taData2 As New Data2TableAdapter
''' tat.AddTableAdapter(taData1)
''' tat.AddTableAdapter(taData2)
''' tat.BeginTransaction()
''' Try
''' taData1.Insert......
''' taData2.Insert......
''' tat.Commit()
''' Catch ex As Exception
''' tat.Rollback()
''' End Try
''' </remarks>
Public Class TableAdapterTransactor
Implements IDisposable
Private _conn As SqlConnection = Nothing
Private _trans As SqlTransaction = Nothing
Private ReadOnly _tableAdapters As New List(Of Object)
Public Function JoinSQLCommand() As SqlCommand
Dim cmd = New SqlCommand
cmd.Connection = Me._conn
cmd.Transaction = Me._trans
Return cmd
End Function
#Region "Pプロシージャ - GetConnection [TableAdapterのConnectionを取得する]"
''' <summary>
''' TableAdapterのConnectionを取得する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <returns>取得したConnection</returns>
''' <remarks></remarks>
Private Function GetConnection(tableAdapter As Object) As SqlConnection
Dim type As Type = tableAdapter.GetType()
Dim connectionProperty As PropertyInfo =
type.GetProperty("Connection", BindingFlags.NonPublic Or BindingFlags.Instance)
connectionProperty = type.GetProperty("Connection",
BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)
Dim connection =
CType(connectionProperty.GetValue(tableAdapter, Nothing), SqlConnection)
Return connection
End Function
#End Region
#Region "Pプロシージャ - SetConnection [TableAdapterのConnectionを設定する]"
''' <summary>
''' TableAdapterのConnectionを設定する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <param name="connection">Connection</param>
''' <remarks></remarks>
Private Sub SetConnection(tableAdapter As Object, connection As SqlConnection)
Dim type As Type = tableAdapter.GetType()
Dim connectionProperty = type.GetProperty("Connection")
connectionProperty.SetValue(tableAdapter, connection, Nothing)
End Sub
#End Region
#Region "Pプロシージャ - GetAdapter [TableAdapterのDataAdapterを取得する]"
''' <summary>
''' TableAdapterのDataAdapterを取得する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <returns>取得したDataAdapter</returns>
''' <remarks></remarks>
Private Function GetAdapter(tableAdapter As Object) As SqlDataAdapter
Dim type As Type = tableAdapter.GetType()
Dim adapterProperty As PropertyInfo =
type.GetProperty("_adapter", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim adapter =
CType(adapterProperty.GetValue(tableAdapter, Nothing), SqlDataAdapter)
Return adapter
End Function
#End Region
#Region "Pプロシージャ - SetAdapter [TableAdapterのDataAdapterを設定する]"
''' <summary>
''' TableAdapterのDataAdapterを設定する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <param name="adapter">DataAdapter</param>
''' <remarks></remarks>
Private Sub SetAdapter(tableAdapter As Object, adapter As SqlDataAdapter)
Dim type As Type = tableAdapter.GetType()
Dim adapterProperty As PropertyInfo =
type.GetProperty("_adapter", BindingFlags.NonPublic Or BindingFlags.Instance)
adapterProperty.SetValue(tableAdapter, adapter, Nothing)
End Sub
#End Region
#Region "Pプロシージャ - InitAdapter [TableAdapterのInitAdapterメソッドを呼び出す]"
''' <summary>
''' TableAdapterのInitAdapterメソッドを呼び出す
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <remarks>利用しない</remarks>
Private Sub InitAdapter(tableAdapter As Object)
Dim type As Type = tableAdapter.GetType()
Dim mi As MethodInfo =
type.GetMethod("InitAdapter", BindingFlags.NonPublic Or BindingFlags.Instance)
mi.Invoke(tableAdapter, Nothing)
End Sub
#End Region
#Region "Pプロシージャ - InitCommandCollection [TableAdapterのInitCommandCollectionメソッドを呼び出す]"
''' <summary>
''' TableAdapterのInitCommandCollectionメソッドを呼び出す
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <remarks>利用しない</remarks>
Private Sub InitCommandCollection(tableAdapter As Object)
Dim type As Type = tableAdapter.GetType()
Dim mi As MethodInfo =
type.GetMethod("InitCommandCollection", BindingFlags.NonPublic Or BindingFlags.Instance)
mi.Invoke(tableAdapter, Nothing)
End Sub
#End Region
#Region "Pプロシージャ - SetTransactionCommands [TableAdapterのCommandCollectionのTransactionを設定する]"
''' <summary>
''' TableAdapterのCommandCollectionのTransactionを設定する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <param name="transaction">Transaction</param>
''' <remarks></remarks>
Private Sub SetTransactionCommands(tableAdapter As Object, transaction As SqlTransaction)
Dim type As Type = tableAdapter.GetType()
Dim commandsProperty As PropertyInfo =
type.GetProperty("CommandCollection", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim commands =
CType(commandsProperty.GetValue(tableAdapter, Nothing), SqlCommand())
For Each command As SqlCommand In commands
command.Transaction = transaction
Next
Me.SetConnection(tableAdapter, transaction.Connection)
End Sub
#End Region
#Region "Pプロシージャ - SetTransactionAdapter [TableAdapterのDataAdapterのTransactionを設定する]"
''' <summary>
''' TableAdapterのDataAdapterのTransactionを設定する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <param name="transaction">Transaction</param>
''' <remarks></remarks>
Private Sub SetTransactionAdapter(tableAdapter As Object, transaction As SqlTransaction)
Dim adp As SqlDataAdapter = Me.GetAdapter(tableAdapter)
If adp.InsertCommand IsNot Nothing Then
adp.InsertCommand.Transaction = transaction
End If
If adp.DeleteCommand IsNot Nothing Then
adp.DeleteCommand.Transaction = transaction
End If
If adp.UpdateCommand IsNot Nothing Then
adp.UpdateCommand.Transaction = transaction
End If
Me.SetAdapter(tableAdapter, adp)
'Me.InitCommandCollection(tableAdapter)
End Sub
#End Region
#Region "メソッド - AddTableAdapter [TableAdapterを追加する]"
''' <summary>
''' TableAdapterを追加する
''' </summary>
''' <param name="tableAdapter">TableAdapter</param>
''' <remarks></remarks>
Public Sub AddTableAdapter(tableAdapter As Object)
Me._tableAdapters.Add(tableAdapter)
End Sub
#End Region
#Region "メソッド - BeginTransaction [トランザクションを開始する]"
''' <summary>
''' トランザクションを開始する
''' </summary>
''' <remarks>開始する前に、AddTableAdapterメソッドで対象となるTableAdapterを追加すること</remarks>
Public Sub BeginTransaction()
Dim counter = 0
For Each adapter As Object In Me._tableAdapters
counter += 1
If counter = 1 Then
Me._conn = Me.GetConnection(adapter)
If Me._conn.State <> ConnectionState.Open Then
Me._conn.Open()
End If
Me._trans = Me._conn.BeginTransaction()
End If
Me.SetConnection(adapter, Me._conn)
Me.SetTransactionAdapter(adapter, Me._trans)
Me.SetTransactionCommands(adapter, Me._trans)
Next
End Sub
''' <summary>
''' 既に開始しているトランザクション処理にTableAdapterを参加させる
''' </summary>
''' <param name="tableAdapter"></param>
Public Sub AddTabledapterAfterBeginTransaction(tableAdapter As Object)
If Me._conn Is Nothing Then Exit Sub
For Each adp As Object In Me._tableAdapters
If tableAdapter Is adp Then Exit Sub
Next
Me.SetConnection(tableAdapter, Me._conn)
Me.SetTransactionAdapter(tableAdapter, Me._trans)
Me.SetTransactionCommands(tableAdapter, Me._trans)
AddTableAdapter(tableAdapter)
End Sub
''' <summary>
''' トランザクションを開始する
''' </summary>
''' <param name="conn">コネクション</param>
''' <remarks></remarks>
Public Sub BeginTransaction(conn As SqlConnection)
Me._conn = conn
If Me._conn.State <> ConnectionState.Open Then
Me._conn.Open()
End If
Me._trans = Me._conn.BeginTransaction()
For Each adapter As Object In Me._tableAdapters
Me.SetConnection(adapter, Me._conn)
Me.SetTransactionAdapter(adapter, Me._trans)
Me.SetTransactionCommands(adapter, Me._trans)
Next
End Sub
''' <summary>
''' トランザクションを開始する
''' </summary>
''' <param name="trans">トランザクション</param>
''' <remarks></remarks>
Public Sub BeginTransaction(trans As SqlTransaction)
Me._conn = trans.Connection
Me._trans = trans
For Each adapter As Object In Me._tableAdapters
Me.SetConnection(adapter, Me._conn)
Me.SetTransactionAdapter(adapter, Me._trans)
Me.SetTransactionCommands(adapter, Me._trans)
Next
End Sub
#End Region
#Region "メソッド - Commit [トランザクションをコミットする]"
''' <summary>
''' トランザクションをコミットする
''' </summary>
''' <remarks></remarks>
Public Sub Commit()
Try
Me._trans.Commit()
Catch ex As Exception
Me._trans.Rollback()
Throw ex
Finally
If (Me._conn.State = ConnectionState.Open) Then
Me._conn.Close()
End If
End Try
End Sub
#End Region
#Region "メソッド - Rollback [トランザクションをロールバックする]"
''' <summary>
''' トランザクションをロールバックする
''' </summary>
''' <remarks></remarks>
Public Sub Rollback()
Me._trans.Rollback()
If (Me._conn.State = ConnectionState.Open) Then
Me._conn.Close()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If (Me._conn.State = ConnectionState.Open) Then
Me._conn.Close()
End If
'破棄するとフォームオープン時にCreateしたテーブルオブジェクトの
'Connectionオブジェクトが破棄されてしまうのでクローズのみ?
' DirectCast(_conn, IDisposable).Dispose()
End Sub
#End Region
End Class
コメントを残す