Seasar DI Container with AOP

トランザクションの自動制御

S2Tx.NETの機能を使って、.NETオブジェクトに対して、 Aspectでトランザクションの自動管理機能を組み込むことができます。 Aspectを適用については、こちらの注意点を確認して下さい。 トランザクションの自動管理には、ローカルトランザクションとMicrosoft 分散トランザクション コーディネータ (MS DTC) の2つをサポートしています。 組む込むことのできるトランザクション属性は次のとおりです。

トランザクション属性

S2Container.NETが標準で用意しているTx.dicon(ローカルトランザクション用)とDTCTx.dicon(MS DTC用)には、 次のトランザクション属性に対応したAdviceが定義されています。 コンポーネント名がAdviceの名前です。

Tx.diconとDTCTx.diconは、source/Seasarフォルダに用意されています。

属性 コンポーネント名 説明
Required LocalTx.RequiredTx
(ローカルトランザクション用)
トランザクションが存在する場合はこれを共有し、 必要に応じて新しいトランザクションを作成します。
DTCTx.RequiredTx
(MS DTC用)
RequiresNew LocalTx.RequiresNewTx
(ローカルトランザクション用)
現在のコンテキストの状態とは関係なく、 新しいトランザクションでコンポーネントを作成します。
DTCTx.RequiresNewTx
(MS DTC用)
Supported LocalTx.SupportedTx
(ローカルトランザクション用)
トランザクションが存在する場合はこれを共有します。
DTCTx.SupportedTx
(MS DTC用)
NotSupported Tx.NotSupportedTx
(ローカルトランザクション用)
制御するトランザクションがないコンテキストでコンポーネントを作成します。
DTCTx.NotSupportedTx
(MS DTC用)

ローカルトランザクション

ローカルトランザクションを利用するためには、Seasar.Extension.Tx.Impl.TxDataSourceから コネクションを取得します。コネクションのオープン、クローズは Advice(MethodInterceptor)が管理するため、 DAO (Data Access Object)では行いません。DAOにTxDataSourceをインジェクションしてもらうために Seasar.Extension.ADO.IDataSource型のプロパティを用意します。

コネクション、トランザクション、コマンド、パラメータのオブジェクトはdataSourceから作成します。 従業員を扱うためのDAOインターフェースとDAO実装クラスは次のようになります。

EmployeeDao.cs

using System;
using System.Data;
using Seasar.Extension.ADO;

namespace Seasar.Examples.Reference.LocalTx
{
    // 従業員を扱うためのDAOインターフェース
    public interface IEmployeeDao
    {
        void Insert();
    }

    // 従業員を扱うためのDAO実装クラス
    public class EmployeeDaoImpl : IEmployeeDao
    {
        private IDataSource dataSource;

        // TxDataSourceをインジェクションしてもらうためのプロパティ
        public IDataSource DataSource
        {
            set { dataSource = value; }
        }

        #region IEmployeeDao メンバ

        public void Insert()
        {
            string sql = "insert into emp2 values(@empno, @ename, @deptnum)";
            
            using(IDbCommand cmd = dataSource.GetCommand(sql,
                      dataSource.GetConnection(), dataSource.GetTransaction()))
            {
                cmd.Parameters.Add(dataSource.GetParameter("@empno", 99));
                cmd.Parameters.Add(dataSource.GetParameter("@ename", "Sugimoto"));
                cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31));
                cmd.ExecuteNonQuery();
                Console.WriteLine("EMPを追加しました。");
            }
        }

        #endregion
    }
}

IEmployeeDaoを実行するクラスは次のようになります。

LocalTxClient.cs

using System;
using Seasar.Framework.Container;
using Seasar.Framework.Container.Factory;

namespace Seasar.Examples.Reference.LocalTx
{
    public class LocalTxClient
    {
        private static readonly String PATH 
                = "Seasar.Examples/Reference/LocalTx/LocalTxClient.dicon";

        public LocalTxClient() {}

        public void Main()
        {
            // S2コンテナを作成します
            IS2Container container = S2ContainerFactory.Create(PATH);
            // S2コンテナを初期化します
            container.Init();
            
            // S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
            IEmployeeDao dao = (IEmployeeDao) container.GetComponent(typeof(IEmployeeDao));

            // 従業員を追加します
            dao.Insert();
        }
    }
}

diconファイルは次のようになります。Required属性のトランザクションを適用するために、 EmployeeDaoImplのaspectにLocalTx.RequiredTxを指定してします。

LocalTxClient.dicon

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    
    <include path="Seasar.Examples/Tx.dicon" />
    
    <component name="LocalTxClient" class="Seasar.Examples.Reference.LocalTx.LocalTxClient" />
    
    <component class="Seasar.Examples.Reference.LocalTx.EmployeeDaoImpl">
        <aspect>LocalTx.RequiredTx</aspect>
    </component>
    
</components>

Tx.dicon

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components namespace="LocalTx">
	
    <include path="Seasar.Examples/Ado.dicon" />

    <component name="SqlDataSource" class="Seasar.Extension.Tx.Impl.TxDataSource">
        <property name="DataProvider">Ado.SqlClient</property>
        <property name="ConnectionString">
            "Server=SQL Serverの名称;database=s2dotnetdemo;Integrated Security=SSPI"
        </property>
    </component>

    <component class="Seasar.Extension.Tx.Impl.TransactionContext" />

    <component name="RequiredTx" class="Seasar.Extension.Tx.TransactionInterceptor">
        <arg>
            <component class="Seasar.Extension.Tx.Impl.LocalRequiredTxHandler" />
        </arg>
    </component>
	
	・・・

</components>

Ado.dicon

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components namespace="Ado">

	<component name="SqlClient" class="Seasar.Extension.ADO.DataProvider">
		<property name="ConnectionType">"System.Data.SqlClient.SqlConnection"</property>
		<property name="CommandType">"System.Data.SqlClient.SqlCommand"</property>
		<property name="ParameterType">"System.Data.SqlClient.SqlParameter"</property>
		<property name="DataAdapterType">"System.Data.SqlClient.SqlDataAdapter"</property>
	</component>
	
</components>

実行結果

DEBUG トランザクションを開始しました
EMPを追加しました。
DEBUG トランザクションをコミットしました

Microsoft 分散トランザクション コーディネータ (MS DTC)による分散トランザクション

MS DTCによる分散トランザクションを利用するためには、MS DTCが利用できる環境でなければいけません。 ローカルトランザクションのようにTxDataSourceからコネクションをとらないといけないといった制限はありませんが、 データプロバイダに依存しないコーディングをするために、Seasar.Extension.ADO.Impl.DataSourceImpl からコネクション、コマンド、パラメータのオブジェクトを作成します。

MS DTCを利用する場合は、1回のクエリ発行毎にコネクションをクローズしても、トランザクションは維持されます。

従業員を扱うためのDAOインターフェースとDAO実装クラスは次のようになります。

EmployeeDao.cs

using System;
using System.Data;
using Seasar.Extension.ADO;

namespace Seasar.Examples.Reference.DTCTx
{
    // 従業員を扱うためのDAOインターフェース
    public interface IEmployeeDao
    {
        void Insert();
    }

    // 従業員を扱うためのDAO実装クラス
    public class EmployeeDaoImpl : IEmployeeDao
    {
        private IDataSource dataSource;

        // DataSourceImplをインジェクションしてもらうためのプロパティ
        public IDataSource DataSource
        {
            set { dataSource = value; }
        }

        #region IEmployeeDao メンバ

        public void Insert()
        {
            using(IDbConnection cn = dataSource.GetConnection())
            {
                cn.Open();
                string sql = "insert into emp2 values(@empno, @ename, @deptnum)";
                using(IDbCommand cmd = dataSource.GetCommand(sql,cn))
                {
                    cmd.Parameters.Add(dataSource.GetParameter("@empno", 99));
                    cmd.Parameters.Add(dataSource.GetParameter("@ename", "Sugimoto"));
                    cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31));
                    cmd.ExecuteNonQuery();
                    Console.WriteLine("EMPを追加しました。");
                }
            }
        }

        #endregion
    }
}

IEmployeeDaoを実行するクラスは次のようになります。

DTCTxClient.cs

using System;
using Seasar.Framework.Container;
using Seasar.Framework.Container.Factory;

namespace Seasar.Examples.Reference.DTCTx
{
    public class DTCTxClient
    {
        private static readonly String PATH 
                = "Seasar.Examples/Reference/DTCTx/DTCTxClient.dicon";

        public DTCTxClient() {}

        public void Main()
        {
            // S2コンテナを作成します
            IS2Container container = S2ContainerFactory.Create(PATH);
            // S2コンテナを初期化します
            container.Init();
            
            // S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
            IEmployeeDao dao = (IEmployeeDao) container.GetComponent(typeof(IEmployeeDao));

            // 従業員を追加します
            dao.Insert();
        }
    }
}

diconファイルは次のようになります。Required属性のトランザクションを適用するために、 EmployeeDaoImplのaspectにDTCTx.RequiredTxを指定します。

DTCTxClient.dicon

<?xml version="1.0" encoding="utf-8" ?> 
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    
    <include path="Seasar.Examples/DTCTx.dicon" />
    
    <component name="DTCTxClient" class="Seasar.Examples.Reference.DTCTx.DTCTxClient" />
    
    <component class="Seasar.Examples.Reference.DTCTx.EmployeeDaoImpl">
        <aspect>DTCTx.RequiredTx</aspect>
    </component>
    
</components>

DTCTx.dicon

<?xml version="1.0" encoding="utf-8" ?> 
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components namespace="DTCTx">

    <include path="Seasar.Examples/Ado.dicon" />

    <component name="SqlDataSource" class="Seasar.Extension.ADO.Impl.DataSourceImpl">
        <property name="DataProvider">Ado.SqlClient</property>
        <property name="ConnectionString">
            "Server=SQL Serverの名称;database=s2dotnetdemo;Integrated Security=SSPI"
        </property>
    </component>

    <component name="DTCTransactionStateHandler" 
            class="Seasar.Extension.Tx.Impl.DTCTransactionStateHandler" />
            
    <component name="RequiredTx" class="Seasar.Extension.Tx.TransactionInterceptor">
        <arg>
            <component class="Seasar.Extension.Tx.Impl.DTCRequiredTxHandler" />
        </arg>
    </component>

    ・・・
    
</components>

Ado.diconと実行結果は、ローカルトランザクションの場合と同じです。

S2Container.NETには、Tx.diconおよびDTCTx.diconがあらかじめ用意(source/Seasar)されています。 Adviceのコンポーネント名をaspectタグのボディに指定するだけなので簡単です。 簡単にトランザクション管理機能が組み込めることがわかってもらえたと思います。

例外発生時の動作

Tx.diconおよびDTCTx.dionに定義されているAdviceは、コンポーネントが例外をスローした場合はトランザクションをロールバックします.