2

現在のセットアップから始めて、何を達成しようとしているのかを説明しましょう。NHibernate を使用しており、Ninject で IRepository/IUnitOfWork パターンを実装しようとしています。ASP.Net、WCF、またはその他のコードを使用しているアプリケーションで一般的に機能することが理想的です。

IUnitOfWork

public interface IUnitOfWork
{
    object Add(object obj);//all other supported CRUD operations we want to expose
    void Commit();
    void Rollback();
}

UnitOfWork

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ISession _session;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        _session = _sessionFactory.OpenSession();
        _transaction = _session.BeginTransaction();
    }

    public object Add(object obj)
    {
        return _session.Save(obj);
    }

    public void Commit()
    {
        if(!_transaction.IsActive)
        {throw new Exception("some error");}
        _transaction.Commit();
    }

    public void Rollback()
    {
        if (!_transaction.IsActive)
        {
            throw new Exception("some other error");
        }
        _transaction.Rollback();
    }
}

Iリポジトリ

public interface IRepository<TEntity, TId> where TEntity : class
{
    TId Add(TEntity item);//add other missing CRUD operations
}

GenericRepository

public class GenericRepository<TEntity, TId> : IRepository<TEntity, TId>
    where TEntity : class
{
    public TId Add(TEntity item)
    {
        throw new NotImplementedException();
    }
}

IOC コンテナーとして Ninject を使用しています。目標は、UnitOfWork の作成のライフ サイクルで同じ IUnitOfWork を再利用することです。呼び出し元のアプリケーションが何であれ、実装されているライフサイクルが機能するようにしたいのですが、それ以外の場合は、オンラインのほとんどの提案のように InRequestScope を使用しただけです。私はこのようなことをすることができました:

//constructor
public MyService(IUnitOfWork uow, IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
    _uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}

//method in same class
public void DoSomeWork()
{
    _userRepo.Add(someUser);
    _catRepo.Add(someCat);
    _uow.Commit();

    //rollback on error
}

そして私のバインディングは次のように設定されています:

Bind<IUnitOfWork>.To<UnitOfWork>().InCallScope();
Bind(typeof(IRepository<,>)).To(typeof(GenericRepository<,>));

そして、このバインディング構成は実際には上記の に対して機能MyServiceし、コンストラクターで一度 UnitOfWork を作成し、IRepo impl に対しても同じ UnitOfWork を使用します。これは、それらが実際に何層下にあるかに関係ありません。

しかし、私ができるようにしたいのは、IUnitOfWork をアプリケーションから完全に隠すことです。メソッドの上に配置できる TransactionAttribute を提供すると、エントリ時に IUnitOfWork が作成され、その同じインスタンスが、TransactionAttribute のスコープ内の IUnitOfWork に対する今後のすべての要求に挿入されます。そして、それに応じてコミットとロールバックを処理します。したがって、前のコードは次のようになります。

//constructor
public MyService(IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
    _uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}

//method in same class
[Transaction]
public void DoSomeWork()
{
    _userRepo.Add(someUser);
    _catRepo.Add(someCat);
}

このような[Transaction]でメソッドをマークできるようにする、私ができるバインディングセットアップの種類はありますか? 私は IUnitOfWork と IRepository のいくつかのマイナーな再構築にオープンであり、サービス層のコードは単なるスクラップ コードであるため、非常に柔軟に対応できます。

4

1 に答える 1

0

まず、注射はうまくいかない

[Transaction]
public void DoSomeWork()
{
      _userRepo.Add(someUser);
      _catRepo.Add(someCat);
}

結局のところ、コンテナーは、ユーザーが呼び出すメソッドの種類と、いつ呼び出すかを認識できません。

さらに話を進めると、WebApps、WCF、.. おそらく何らかのクォーツの仕事かどうか: NHibernate の有効期間をオブジェクトの有効期間に結び付けるかどうかを決定する必要がIUnitOfWorkありRepositoryますSession。それを ( のように) 使用するか、サービスをより長く存続させたい場合 (シングルトンなど)、次のようなメソッド呼び出し用にMyService新しい NHibernate を作成します。Session

[Transaction]
public void DoSomeWork()
{
    _userRepo.Add(someUser);
    _catRepo.Add(someCat);
}

これは、実際にはFodyまたはPostSharpを使用してAOPによって実現される可能性があります。または、デコレータ パターンを使用してこれを実現することもできます (たとえば、こちらを参照してください)。ただし、私の記憶が正しければ、現在、ninject には easz デコレータの処理をサポートするための機能がいくつか欠けています。

属性の代わりに、[Transaction]すべてを明示的に制御することから始めることができます:

public interface IUnitOfWork
{
    IUnitOfWorkSession Begin(); // starts a session and transaction
}

public interface IUnitOfWorkSession : IDisposable
{
   void Commit();
   void Dispose(); // performs rollback in case no commit was performed
}

Session他のオブジェクトの nhibernate にアクセスする必要があるため、オブジェクトをインスタンス化する方法/時期とは関係がないため、コンテナーから独立してそこにアクセスする方法も必要に_userRepoなります。もちろん、次のように渡すこともできます。_catRepoSession

public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin())
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit();
    }
}

しかし、それは本当にクールではありません。したがって、代わりにThreadLocal(async/await を使用している場合はこれが問題になる可能性があります) またはSynchronizationContext-local ストレージのようなものを使用する必要があります。

ここまでたどり着いたら、AOP の実行を検討できます。AOP では、次の 2 つの処理を 2 つ行う必要があります。

  • IUnitOfWork へのアクセスを取得します。
    • 追加のctor引数として織り込むことで実行できます
    • または、既に存在するかどうかを確認できます。存在する場合はこれを使用できます。存在しない場合は、引数をスローまたは織り込むことができます...
  • 上記と同じコードで装飾されたメソッドをラップします。
public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) // weave this
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit(); // weave this
    }
}
于 2014-10-31T12:23:19.693 に答える