TableAdapter親クラスの変更
TableAdapterクラスには親クラスを差し替える機能があるのは知っていたのですが、使い道は全く思いつきませんでした。
(データセットデザイナで TableAdapterにあるBaseClassプロパティを変更することで親を変更できる)
プログラミング MS ADO .NET2.0 (マイクロソフト公式解説書)
- 作者: デビッドセッパ,David Sceppa,日本ユニテック
- 出版社/メーカー: 日経BP社
- 発売日: 2007/07/26
- メディア: 単行本
- 購入: 2人 クリック: 66回
- この商品を含むブログ (13件) を見る
でも有効利用しているサンプルを見つけました。
http://www.codeproject.com/useritems/typed_dataset_transaction.asp
TableAdapterでマニュアルトランザクションを使う簡単な方法はpartialクラスを使いTransactionを公開します。
しかし、TableAdapterごとに書くのは面倒・・・
ということで、親クラスでトランザクションの制御をするクラスを作ったようです。
そのソース
// ---------------------------------------------------------------------------- // // TransactionSupport // // Original author: Mike Pagel // // After ideas given in // http://www.codeproject.com/useritems/typed_dataset_transaction.asp // http://entwickler-forum.de/showpost.php?p=2032&postcount=2 // // ---------------------------------------------------------------------------- using System.Data.SqlClient; using System.Data; using System.Reflection; namespace BizWiz { /// <summary> /// Transaction support for generated table adapters. /// </summary> /// <remarks> /// This class adds transaction support to table adapters. It is used by changing the base /// class of a table adapter from Component to this class. The implementation of this class /// then accesses the derived table adapter's properties through reflection. /// </remarks> public abstract class TransactionSupport : System.ComponentModel.Component { // -------------------------------------------------------------------- #region Reflective access to table adapter properties // -------------------------------------------------------------------- private SqlConnection Connection { // Access to propertis as public and non-public as generated table-adapter // scope seems to be different for different installations: // http://www.codeproject.com/useritems/transactionta.asp?msg=2225021#xx2225021xx get { return (SqlConnection)GetType().GetProperty( "Connection", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( this, null ); } set { GetType().GetProperty( "Connection", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).SetValue( this, value, null ); } } // -------------------------------------------------------------------- private SqlDataAdapter Adapter { get { return (SqlDataAdapter)GetType().GetProperty( "Adapter", BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( this, null ); } } // -------------------------------------------------------------------- private SqlCommand[] CommandCollection { get { return (SqlCommand[])GetType().GetProperty( "CommandCollection", BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( this, null ); } } #endregion // -------------------------------------------------------------------- #region Properties // -------------------------------------------------------------------- /// <summary> /// Transaction of this table adapter. /// </summary> /// <remarks> /// This property is used to share a transaction and its associated connection /// across multiple table adapters. The typical pattern is shown in the sample /// code below. /// </remarks> /// <example> /// XTableAdapter xta = new XTableAdapter(); /// YTableAdapter yta = new YTableAdapter(); /// /// xta.BeginTransation(); /// yta.Transation = xta.Transaction; /// try /// { /// // perform xta and yta modifications here. /// xta.CommitTransaction(); /// } /// catch( Exception ) /// { /// xta.RollbackTransaction(); /// } /// </example> public SqlTransaction Transaction { get { return transaction; } set { // attach transaction to all commands of this adapter: if( CommandCollection != null ) { foreach( SqlCommand command in CommandCollection ) { command.Transaction = value; } } if( Adapter.InsertCommand != null ) { Adapter.InsertCommand.Transaction = value; } if( Adapter.UpdateCommand != null ) { Adapter.UpdateCommand.Transaction = value; } if( Adapter.DeleteCommand != null ) { Adapter.DeleteCommand.Transaction = value; } // also set connection of this adapter accordingly: if( value != null ) { Connection = value.Connection; } else { // only clear connection if it was attached to // transaction before: if( transaction != null ) Connection = null; } transaction = value; } } #endregion // -------------------------------------------------------------------- #region Operations // -------------------------------------------------------------------- public void BeginTransaction() { // Open the connection, if needed if( Connection.State != ConnectionState.Open ) Connection.Open(); // Create the transaction and assign it to the Transaction property Transaction = Connection.BeginTransaction(); } // -------------------------------------------------------------------- public void CommitTransaction() { // Commit the transaction Transaction.Commit(); // Close the connection Connection.Close(); } // -------------------------------------------------------------------- public void RollbackTransaction() { // Rollback the transaction Transaction.Rollback(); // Close the connection Connection.Close(); } #endregion // -------------------------------------------------------------------- #region Fields // -------------------------------------------------------------------- /// <summary> /// Fields supporting properties. /// </summary> private SqlTransaction transaction; #endregion // -------------------------------------------------------------------- } }
なるほど、リフレクションなら実行時にコネクション取れるわけですね。
なるほど。
使い方
using (TransactionScope transaction = new TransactionScope()) { adapter1.Update(table1); adapter2.Update(table2); transaction.Complete(); }
でも思ったのですがリフレクション使うんだったら、以下のようなstaticメソッド作って
public static void SetTransaction(Component ta, SqlTransaction tx) { // コネクション取得 Type typTA = ta.GetType(); PropertyInfo propCon = typTA.GetProperty("Connection", BindingFlags.NonPublic | BindingFlags.Instance); if (propCon == null) { throw new System.ArgumentException(); } // テーブルアダプターの取得 PropertyInfo propAdapter = typTA.GetProperty("Adapter", BindingFlags.NonPublic | BindingFlags.Instance); if (propAdapter == null) { throw new System.ArgumentException(); } Object objAdapter = propAdapter.GetValue(ta, null); if (objAdapter is SqlDataAdapter) { // コネクションをテーブルアダプターにセットする propCon.SetValue(ta, tx.Connection, null); SqlDataAdapter adapter = (SqlDataAdapter)objAdapter; // 各コマンドにトランザクションをセットする SqlCommand[] CommandCollection = (SqlCommand[])typTA.GetProperty( "CommandCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ta, null); if (CommandCollection != null) { foreach (SqlCommand command in CommandCollection) { command.Transaction = tx; } } if (adapter.UpdateCommand != null) { adapter.UpdateCommand.Transaction = tx; } if (adapter.InsertCommand != null) { adapter.InsertCommand.Transaction = tx; } if (adapter.DeleteCommand != null) { adapter.DeleteCommand.Transaction = tx; } if (adapter.SelectCommand != null) { adapter.SelectCommand.Transaction = tx; } } }
こんな感じで使う方が簡単じゃないかと。
(結局TableAdapterの親クラスをいちいち変更するのが面倒だと思う)
using (SqlConnection con = new SqlConnection("〜〜〜")) { con.Open(); SqlTransaction tx = con.BeginTransaction(); DBUtil.SetTransaction(ta1, tx); DBUtil.SetTransaction(ta2, tx); ta1.Update(); ta2.Update(); tx.Commit(); }