15

私は次の実装を持っていますが、セッションとトランザクションに NHibernate を正しく使用しているかどうかについてフィードバックをお願いします。

public interface IUnitOfWork : IDisposable
{
    ISession CurrentSession { get; }
    void Commit();
    void Rollback();
}

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

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

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

    public void Commit()
    {
        _transaction.Commit();
    }

    public void Rollback()
    {
        if (_transaction.IsActive) _transaction.Rollback();
    }
}

ニンジェクトバインディング

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();

使用例を次に示します。

public class Repository : IRepository
{
    private readonly ISessionFactory _sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void Add(IObj obj)
    {
        using (var unitOfWork = new UnitOfWork(_sessionFactory))
        {
            unitOfWork.CurrentSession.Save(obj);
            unitOfWork.Commit();
        }         
    }
}

以前の実装では、IUnitOfWork をリポジトリ コンストラクターに次のように挿入していました。

public Repository(IUnitOfWork unitOfWork)
    {...

しかし、Dispose() メソッドは実行されず、後続の呼び出しで次の例外がスローされます。「破棄されたオブジェクトにアクセスできません。オブジェクト名: 'AdoTransaction'.」

4

3 に答える 3

35

最初の観察: リポジトリは作業単位をコミットすべきではありません。これは、作業単位パターンの要点全体を無効にします。変更をリポジトリ内にすぐに保存することで、NHibernate セッションを「細かく管理」できます。

作業単位は、アプリケーション/サービス レイヤーのスタックの上位で参照する必要があります。これにより、場合によっては異なるリポジトリで複数のアクションを実行し、最後にすべてを一度にコミットするアプリケーション コードを作成できます。

UnitOfWork クラス自体は問題ないように見えますが、本当に必要かどうかを自問する必要があります。NHibernate では、ISession が作業単位です。あなたの UnitOfWork クラスは多くの価値を追加していないようです (特に CurrentSession プロパティを公開しているため)

しかし、あなたはそれが寿命について考える必要があります。この点であなたは間違っていると思います。セッション ライフタイム管理は、開発しているアプリケーションの種類によって異なります。Web アプリでは、通常、リクエストごとに作業単位が必要です (「nhibernate session per request」でググるとよいでしょう)。デスクトップ アプリではもう少し複雑で、ほとんどの場合、「画面ごとのセッション」または「ビジネス トランザクションごとの会話」が必要になります。

于 2010-11-24T20:57:53.947 に答える
8

私は主に CRUD タイプのアプリケーションを使用しており、Unit Of Work with Repository パターンを実装しましたが、セッション/トランザクションの分割から逃れることはできませんでした。セッションとトランザクションには異なる有効期間が必要です。デスクトップの世界では、通常、セッションは「画面ごと」であり、トランザクションは「ユーザー アクションごと」です。

詳細については、この優れた記事を参照してください。

だから私が最終的に得たのは:

  • IUnitOfWork-> セッションをラップし、実装しますIDisposable
  • IAtomicUnitOfWork-> トランザクションをラップし、実装しますIDisposable
  • IRepository-> Get、Save、Delete、およびクエリ アクセスを提供します

を構築するには が必要で、IUnitOfWorkを構築するにIAtomicUnitOfWorkは が必要になるように作成したので、適切なトランザクション管理が強制されます。独自のインターフェースを実装することで得られたのは、これだけです。IAtomicUnitOfWorkIRepository

jeroenhが言ったように、あなたはほとんど同じように使えますがISessionITransaction最終的には、私が定義したインターフェースに対してすべてのコードを書く方が少し良いと感じました.

于 2010-11-25T01:54:13.577 に答える
4

答えの重要な部分は、トランザクション サイズをどうしたいかということです。現在(jeroenhが示しているように)、トランザクションはリポジトリでのメソッド呼び出しごとです。これは非常に小さく、おそらく必要ありません。ASP.MVC アプリケーションを作成しましたが、単一の http 要求のすべてを含むトランザクション サイズを使用しています。これは、複数のデータベースの読み取り/更新である可能性があります。IOC に同じ作業単位と Ninject を使用しています。見てください、多分何かがあなたの問題に役立つでしょう:

http://bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/

http://bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/

http://bobcravens.com/2010/09/the-repository-pattern-part-2/

http://bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/

お役に立てれば。

ボブ

于 2010-11-25T16:12:06.540 に答える