Quillでトランザクション

Quillトランザクションをするためにはトランザクションをかけたいメソッドに[Transaction]属性をつけます
またApp.config内のdataSourcesタグのデータソースの設定でタグ内にSeasar.Extension.Tx.Impl.TxDataSourceImpl
を指定します。

    <dataSources>
      <!-- Oracle -->
      <dataSource name="Oracle">
        <provider>Oracle</provider>
        <connectionString>...</connectionString>
        <class>Seasar.Extension.Tx.Impl.TxDataSourceImpl</class>
      </dataSource>
    </dataSources>

このようにしておいてトランザクションをかけたいメソッドに[Transaction]属性
をつけると、このメソッドから例外が発生したときにRollbackされます

    [Transaction]
    public void RegisterData()
    {
        try
        {
            // データ登録ロジック
        }
        catch(Exception ex)
        {
            throw;
        }
    }

複数のデータソースでトランザクションをかける場合はデータソース毎にカスタムトランザクション設定クラスの実装が必要です。

public class OracleTransactionSetting : AbstractTransactionSetting
{
    protected override void SetupTransaction(IDataSource dataSource)
    {
        //  TransactionContext
        _transactionContext = new TransactionContext();
        TransactionContext txContext = (TransactionContext)_transactionContext;
        txContext.DataSouce = dataSource;
        txContext.IsolationLevel = IsolationLevel.ReadCommitted;

        //  TransactionContextを使用するデータソースにも設定
        Type dataSourceType = dataSource.GetType();
        if (typeof(SelectableDataSourceProxyWithDictionary).IsAssignableFrom(
            dataSourceType))
        {
            ((SelectableDataSourceProxyWithDictionary)dataSource)
                .SetTransactionContext(txContext);
        }
        else if (typeof(TxDataSource).IsAssignableFrom(dataSourceType))
        {
            ((TxDataSource)dataSource).Context = txContext;
        }

        //  TransactionInterceptor
        LocalRequiredTxHandler handler = new LocalRequiredTxHandler();
        handler.Context = txContext;
        _transactionInterceptor = new TransactionInterceptor(handler);
        ((TransactionInterceptor)_transactionInterceptor).TransactionStateHandler
            = txContext;
    }
    public override String DataSourceName
    {
        get { return "Oracle"; } // App.config内のデータソース設定の<dataSource name="Oracle"> の記述に合わせる
    }
}

トランザクションをかけたい箇所に[Transaction]属性をつけます

    [Transaction(typeof(OracleTransactionSetting))]
    public void RegisterData()
    {
        try
        {
            // データ登録ロジック
        }
        catch(Exception ex)
        {
            throw;
        }
    }

[seasar-dotnet:1210] Quill で複数データソースでのトランザクション制御に失敗するで議論されていますが、複数のデータソースがある場合トランザクションに失敗するようです。

    public class OracleTransactionSetting : AbstractTransactionSetting
    {
        protected override void SetupTransaction(IDataSource dataSource)
        {
            //  TransactionContext
            _transactionContext = TransactionContextCreator.GetTransactionContext(dataSource);
            TransactionContext txContext = (TransactionContext)_transactionContext;

            //  TransactionContextを使用するデータソースにも設定
            Type dataSourceType = dataSource.GetType();
            if (typeof(SelectableDataSourceProxyWithDictionary).IsAssignableFrom(
                dataSourceType))
            {
                ((SelectableDataSourceProxyWithDictionary)dataSource)
                    .SetTransactionContext(txContext);
            }
            else if (typeof(TxDataSource).IsAssignableFrom(dataSourceType))
            {
                ((TxDataSource)dataSource).Context = txContext;
            }

            //  TransactionInterceptor
            LocalRequiredTxHandler handler = new LocalRequiredTxHandler();
            handler.Context = txContext;
            _transactionInterceptor = new TransactionInterceptor(handler);
            ((TransactionInterceptor)_transactionInterceptor).TransactionStateHandler
                = txContext;
        }
        public override String DataSourceName
        {
            get { return "Oracle"; }
        }
    }
    public class PostgreSQLTransactionSetting : AbstractTransactionSetting
    {
        protected override void SetupTransaction(IDataSource dataSource)
        {
            //  TransactionContext
            _transactionContext = TransactionContextCreator.GetTransactionContext(dataSource);
            TransactionContext txContext = (TransactionContext)_transactionContext;

            //  TransactionContextを使用するデータソースにも設定
            Type dataSourceType = dataSource.GetType();
            if (typeof(SelectableDataSourceProxyWithDictionary).IsAssignableFrom(
                dataSourceType))
            {
                ((SelectableDataSourceProxyWithDictionary)dataSource)
                    .SetTransactionContext(txContext);
            }
            else if (typeof(TxDataSource).IsAssignableFrom(dataSourceType))
            {
                ((TxDataSource)dataSource).Context = txContext;
            }

            //  TransactionInterceptor
            LocalRequiredTxHandler handler = new LocalRequiredTxHandler();
            handler.Context = txContext;
            _transactionInterceptor = new TransactionInterceptor(handler);
            ((TransactionInterceptor)_transactionInterceptor).TransactionStateHandler
                = txContext;
        }
        public override String DataSourceName
        {
            get { return "PostgreSQL"; }
        }
    }
    public static class TransactionContextCreator
    {
        private static TransactionContext _transactionContext = null;

        public static ITransactionContext GetTransactionContext(IDataSource dataSource)
        {
            if (_transactionContext == null)
            {
                _transactionContext = new TransactionContext();
                _transactionContext.DataSouce = dataSource;
                _transactionContext.IsolationLevel = IsolationLevel.ReadCommitted;
            }
            return _transactionContext;
        }
    }

このようにすると良いらしいです

Quillで複数データベースにアクセス

Quillで複数のデータベースにアクセスするための設定に手間取ったので
その結果を書いておきます。

複数のデータベース(データソース)に接続するために必要なものは

1.App.config内のdataSourcesタグにデータソースの設定を記述

Quillで簡単DI+AOP - S2Container.NET にあるように設定ファイル(App.config)の
quill」セクション内の「dataSources」タグ内に各データソースの設定を書きます。

    <dataSources>
      <!-- Oracle -->
      <dataSource name="Oracle">
        <provider>Oracle</provider>
        <connectionString>...</connectionString>
        <class>Seasar.Extension.ADO.Impl.DataSourceImpl</class>
      </dataSource>
      <!-- PostgreSQL -->
      <dataSource name="PostgreSQL">
        <provider>PostgreSQL</provider>
        <connectionString>...</connectionString>
        <class>Seasar.Extension.ADO.Impl.DataSourceImpl</class>
      </dataSource>
    </dataSources>

上の設定内容の中のタグの中に指定するクラスは
Seasar.Extension.ADO.IDataSourceに対する実装クラス名を指定する必要があり
データソース毎に別のクラスを指定しないといけません。
Seasar.Extension.ADO.Impl.DataSourceImplやSeasar.Extension.Tx.Impl.TxDataSourceImplの継承クラスをデータソース毎に作成し、コンストラクタを記述します。
koyakさんのご指摘によりカスタムデータソース実装クラスは不要であることがわかりました。
koyakさんありがとうございます。

2.データソース毎にSeasar.Quill.Dao.IDaoSetting実装クラスを作成する

    public class OracleDaoSetting : Seasar.Quill.Dao.Impl.AbstractDaoSetting
    {
        protected override void SetupDao(IDataSource dataSource)
        {
            BasicCommandFactory commandFacoty = new BasicCommandFactory();
            BasicDataReaderFactory dataReaderFactory = new BasicDataReaderFactory(commandFacoty);
            FieldAnnotationReaderFactory annotationReaderFactory = new FieldAnnotationReaderFactory();
            _daoMetaDataFactory = new DaoMetaDataFactoryImpl( dataSource, commandFacoty, annotationReaderFactory, dataReaderFactory);
            _daoInterceptor = new S2DaoInterceptor(_daoMetaDataFactory);
        }
        public override String DataSourceName
        {
            get { return "Oracle"; } // App.config内のデータソース設定の<dataSource name="Oracle"> の記述に合わせる
        }
    }

3.DaoクラスにS2Dao属性を使用し、カスタム設定を適用させる

    [S2Dao(typeof(OracleDaoSetting))] 
    [Implementation]
    [Bean(typeof(HogeDto))]
    public interface IOracleDao
    {
       int insert(HogeDto dto);
    }

このような設定で複数のデータソースへのアクセスができました。

QuillでSQLiteを使ってみる

QuillSQLiteをアクセスしようと思い調べてみたが
Seasar.NETでは対応されているもののQuillでは未対応らしい。

ただ独自のプロバイダクラスを作成することで何とかなるようだ
http://s2container.net.seasar.org/ja/quill.html#nodicon_assembly

Seasarメーリングリストで問い合わせたら、すぐに回答いただきました、感謝です。
以下MLからの引用です。

1. http://sqlite.phxsoftware.com/ からSQLite.NETをダウンロードしてインストールする
2. Seasar.Extension.ADO.DataProviderを継承したクラスを作成する。
3. 2.で作成したクラスのコンストラクタの中でConnectionType 、CommandType、ParameterType、DataAdapterTypeプロパティにSQLite用のクラス名を名前空間付きで指定する。

public class SQLite: Seasar.Extension.ADO.DataProvider
   {
       public SQLite()
       {
           ConnectionType = "System.Data.SQLite.SQLiteConnection";
           CommandType = "System.Data.SQLite.SQLiteCommand";
           ParameterType = "System.Data.SQLite.SQLiteParameter";
           DataAdapterType = "System.Data.SQLite.SQLiteDataAdapter";
       }
   }

4. 設定ファイルのproviderセクションに2.で作成したクラス名を名前空間付きで指定する
5. 設定ファイルのassemblyセクションにSystem.Data.SQLiteを指定する

<configuration>
<quill>
    <assemblys>
      <assembly>Seasar.Quill</assembly>
      <assembly>Seasar.Dxo</assembly>
      <assembly>Seasar.DynamicProxy</assembly>
      <!-- SQLite.NET を使用する場合に必要です。 -->
      <assembly>System.Data.SQLite</assembly>
    </assemblys>
  <dataSources>
    <!-- SQLite -->
    <dataSource name="sqlite">
      <provider>xxx.xxxx.SQLite</provider>
      <connectionString>"Data Source=test.db;"</connectionString>
      <class>Seasar.Extension.Tx.Impl.TxDataSource</class>
    </dataSource>
  </dataSources>
 </quill>
</configuration>

Quill試してみた

S2Conteiner.NETは今まで使っていたのですが、Seasarのメーリングリストに
Quillとの使い分けみたいなことが流れていたので読んでみると
「.NETの開発環境においてはQuillを推奨します」というようなこと
が書かれてました。コミッターさんたちもQuillをメインで使用しているらしく
S2Container.NETについてはサポートが不安だったのでQuillを試してみました
あれだけ大変だったdiconファイルがまったくなくなってびっくり!!
今までIncludeパスの書き間違いで何度も見直して工数取られてたんだけど
ストレスなく開発できそうです。

TestDriven.NETでSetupが動かない

S2Unit.NETを使って単体テストしてますが、VS2005のアドインでTestDriven.NET
を使用しています。

[SetUp, S2]
public void SetUpSeasarTest()
{
    SingletonS2ContainerFactory.Init();
    this.Container = SingletonS2ContainerFactory.Container;
}
[Test, S2]
public void コンテナが取得できるはず()
{
    Assert.IsNotNull(this.Container, "コンテナが取得できるはず");
}

このようにしてテストを行っていたのですがthis.Containerがnullのままで
インスタンスを取得してくれません。原因はSetUpSeasarTestを通らないのが原
因です。
調べた結果このサイトに回答がありました
http://blog.davebouwman.net/2008/08/05/TroubleRunningMbUnitTestsWithTestDrivennetOnVista64.aspx

TestDriven.NetはインストーラではMbUnitを使用する設定をしてくれないようです。

  1. MbUnitをインストールする http://www.mbunit.com/
  2. TestDriven.NETにMbUnitを使用するためのレジストリを登録する

下のレジストリを登録すればよいようです。

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\MutantDesign\TestDriven.NET\TestRunners\MbUnit]
@="10"
"AssemblyPath"="C:\\Program Files\\MbUnit\bin\\MbUnit.AddIn.dll"
"TypeName"="MbUnit.AddIn.MbUnitTestRunner"
"TargetFrameworkAssemblyName_Reference"="MbUnit.Framework"
"Application"="C:\\Program Files\\MbUnit\bin\\MbUnit.GUI.exe"

Oracle 10g XE で接続できない

Oracle 10g Express Edition をインストールして接続しようとしたところ,
以下のようなエラーが発生しました。
「ORA-12514:TNS:リスナーは接続記述子でリクエストされたサービスを現在認識
していません。」
listenerサービスが起動していることも確認しましたが特に問題ないようです。

調べた結果環境変数 TNS_ADMIN に tnsnames.oraのあるフォルダのパスを設定す
る必要があるようです。

これで無事接続可能(ローカルにもリモートにも)になりました。