目次


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

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

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

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

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

EmployeeDao.cs

C#

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
    }
}

VB

Imports System
Imports System.Data
Imports Seasar.Extension.ADO

Namespace Seasar.Examples.Reference.LocalTx

   ' 従業員を扱うためのDAOインターフェース
   Public Interface IEmployeeDao
      Sub Insert()
   End Interface 'IEmployeeDao
   
   ' 従業員を扱うためのDAO実装クラス
   Public Class EmployeeDaoImpl
      Implements IEmployeeDao
      
      Private dataSource As IDataSource
      
      ' TxDataSourceをインジェクションしてもらうためのプロパティ
      Public WriteOnly Property DataSource() As IDataSource
         Set(ByVal value As IDataSource)
            dataSource = value
         End Set
      End Property
       
      Public Sub Insert()

         Dim sql As String = _
             "insert into emp2 values(@empno, @ename, @deptnum)"
         
         Dim cmd As IDbCommand = dataSource.GetCommand(sql, _
            dataSource.GetConnection(), dataSource.GetTransaction())
         Try
            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を追加しました。")
         Finally
            cmd.Dispose()
         End Try
      End Sub
   End Class
End Namespace

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

LocalTxClient.cs

C#

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();
        }
    }
}

VB

Imports System
Imports Seasar.Framework.Container
Imports Seasar.Framework.Container.Factory

Namespace Seasar.Examples.Reference.LocalTx

   Public Class LocalTxClient

      Private Shared PATH As [String] = _
         "Seasar.Examples/Reference/LocalTx/LocalTxClient.dicon"
      
      Public Sub New()
      End Sub
       
      Public Sub Main()

         ' S2コンテナを作成します
         Dim container As IS2Container = S2ContainerFactory.Create(PATH)
         ' S2コンテナを初期化します
         container.Init()
         
         ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
         Dim dao As IEmployeeDao = _
            CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)
         
         ' 従業員を追加します
         dao.Insert()
      End Sub
   End Class
End Namespace

 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="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">

  <!-- .NET Framework Data Provider for SQL Server を使用する場合に必要です。 -->
  <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>
  <component name="DataSource" class="Seasar.Extension.Tx.Impl.TxDataSource">
    <property name="DataProvider">SqlClient</property>
    <property name="ConnectionString">
"Data Source=(local);Initial Catalog=s2dotnetdemo;Integrated Security=SSPI"
    </property>
  </component>

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

実行結果

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

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

 分散トランザクションの注意点ですが、Seasar.dllをCOM+アプリケーションにインストールする必要があります。 COM+アプリケーションへのインストールは自動で行われますが、実行ユーザにその権限がない場合は、 レジストリキーへのアクセスが拒否されたという例外(System.UnauthorizedAccessException)が発生します。 このような例外が発生した場合は、権限を持ったユーザで実行するか、 あらかじめ権限を持ったユーザでCOM+アプリケーションへのインストールを手動で行ってください。

 手動でSeasar.dllをCOM+アプリケーションにインストールするためには .NET サービス インストールツール(RegSvcs.exe)を使います。.NET 2.0 の場合は、 RegSvcs.exeは %windir%\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe に存在します。以下のようにRegSvcs.exeのパラメータにSeasar.dllへのフルパスを渡して、 Seasar.dllをCOM+アプリケーションにインストールすることができます。

  • RegSvcs.exe Seasar.dllへのフルパス

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

EmployeeDao.cs

C#

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
    }
}

VB

Imports System
Imports System.Data
Imports Seasar.Extension.ADO

Namespace Seasar.Examples.Reference.DTCTx

   ' 従業員を扱うためのDAOインターフェース
   Public Interface IEmployeeDao
      Sub Insert()
   End Interface
   
   ' 従業員を扱うためのDAO実装クラス
   Public Class EmployeeDaoImpl
      Implements IEmployeeDao

      Private dataSource As IDataSource
      
      ' DataSourceImplをインジェクションしてもらうためのプロパティ      
      Public WriteOnly Property DataSource() As IDataSource
         Set(ByVal value As IDataSource)
            dataSource = value
         End Set
      End Property
       
      Public Sub Insert()

         Dim cn As IDbConnection = dataSource.GetConnection()
         Try
            cn.Open()
            Dim sql As String = _
                "insert into emp2 values(@empno, @ename, @deptnum)"
            Dim cmd As IDbCommand = dataSource.GetCommand(sql, cn)
            Try
               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を追加しました。")
            Finally
               cmd.Dispose()
            End Try
         Finally
            cn.Dispose()
         End Try

      End Sub

   End Class

End Namespace

DTCTxClient.cs

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

C#

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();
        }
    }
}

VB

Imports System
Imports Seasar.Framework.Container
Imports Seasar.Framework.Container.Factory

Namespace Seasar.Examples.Reference.DTCTx

   Public Class DTCTxClient
      Private Shared PATH As [String] = _
         "Seasar.Examples/Reference/DTCTx/DTCTxClient.dicon"
      
      Public Sub New()
      End Sub
       
      Public Sub Main()

         ' S2コンテナを作成します
         Dim container As IS2Container = S2ContainerFactory.Create(PATH)
         ' S2コンテナを初期化します
         container.Init()
         
         ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
         Dim dao As IEmployeeDao = _
            CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)
         
         ' 従業員を追加します
         dao.Insert()

      End Sub

   End Class

End Namespace

 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="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と実行結果は、ローカルトランザクションの場合と同じです。

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

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

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

EmployeeDao.cs

C#

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

namespace Seasar.Examples.Reference.Tx
{
    // 従業員を扱うための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
    }
}

VB

Imports System
Imports System.Data
Imports Seasar.Extension.ADO

Namespace Seasar.Examples.Reference.Tx

   ' 従業員を扱うためのDAOインターフェース
   Public Interface IEmployeeDao
      Sub Insert()
   End Interface
   
   ' 従業員を扱うためのDAO実装クラス
   Public Class EmployeeDaoImpl
      Implements IEmployeeDao

      Private dataSource As IDataSource
      
      ' DataSourceImplをインジェクションしてもらうためのプロパティ
      Public WriteOnly Property DataSource() As IDataSource
         Set(ByVal value As IDataSource)
            dataSource = value
         End Set
      End Property
       
      Public Sub Insert()

         Dim cn As IDbConnection = dataSource.GetConnection()
         Try
            cn.Open()
            Dim sql As String = _
                "insert into emp2 values(@empno, @ename, @deptnum)"
            Dim cmd As IDbCommand = dataSource.GetCommand(sql, cn)
            Try
               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を追加しました。")
            Finally
               cmd.Dispose()
            End Try
         Finally
            cn.Dispose()
         End Try

      End Sub

   End Class

End Namespace

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

TxClient.cs

C#

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

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

        public TxClient() {}

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

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

VB

Imports System
Imports Seasar.Framework.Container
Imports Seasar.Framework.Container.Factory

Namespace Seasar.Examples.Reference.Tx

   Public Class TxClient
      Private Shared PATH As [String] = _
         "Seasar.Examples/Reference/Tx/TxClient.dicon"
      
      Public Sub New()
      End Sub
       
      Public Sub Main()

         ' S2コンテナを作成します
         Dim container As IS2Container = S2ContainerFactory.Create(PATH)
         ' S2コンテナを初期化します
         container.Init()
         
         ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
         Dim dao As IEmployeeDao = _
            CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)
         
         ' 従業員を追加します
         dao.Insert()

      End Sub

   End Class

End Namespace

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

TxClient.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="TxClient"
            class="Seasar.Examples.Reference.Tx.TxClient" />
    
    <component class="Seasar.Examples.Reference.Tx.EmployeeDaoImpl">
        <aspect>Tx.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="Tx">
    <include path="Seasar.Examples/Ado.dicon" />

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

    ...
    
</components>

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

 S2Container.NETには、Tx.dicon, LocalTx.dicon, DTCTx.diconのサンプルがあらかじめ用意(s2container.net/source/Seasar.Tests)されています。 必要に応じて、ユーザアプリケーションのプロジェクトにコピーしてください。

 Adviceのコンポーネント名をaspectタグのボディに指定するだけなので簡単です。 簡単にトランザクション管理機能が組み込めることがわかって頂けたと思います。

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