4

ASP.NETWebアプリケーションでリポジトリパターンの実装を完了する方法を見つけようとしています。

現在、ドメインクラスごとにリポジトリインターフェイスがあり、そのクラスのインスタンスを読み込んだり保存したりするためのメソッドを定義しています。

各リポジトリインターフェイスは、NHibernateを実行するクラスによって実装されます。Castle Windsorは、web.configに従って、クラスのDIをインターフェースに分類します。実装されたクラスの例を以下に示します。

  public class StoredWillRepository : IStoredWillRepository
  {
    public StoredWill Load(int id)
    {
      StoredWill storedWill;
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        storedWill = session.Load<StoredWill>(id);
        NHibernateUtil.Initialize(storedWill);
      }
      return storedWill;
    }

    public void Save(StoredWill storedWill)
    {
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        using (ITransaction transaction = session.BeginTransaction())
        {
          session.SaveOrUpdate(storedWill);
          transaction.Commit();
        }
      }
    }
  }

前のスレッドで指摘したように、リポジトリクラスは、すべてのメソッドでインスタンス化するのではなく、作業単位コンテナ(つまり、ISession)を受け入れる必要があります。

作業単位コンテナは、必要に応じて(たとえば、プロパティで)各aspxページによって作成されると思います。

次に、Windsorが作成するときに、この作業単位のコンテナーインスタンスがStoredWillRepositoryのコンストラクターに渡されるように指定するにはどうすればよいですか?

それとも、このパターンは完全に間違っていますか?

アドバイスありがとうございます。

デビッド

4

3 に答える 3

1

いくつかのWebアプリで使用されているNHibernate上に構築された永続性フレームワークがあります。IRepositoryこれは、Unityによって提供される具体的なインスタンスを使用してNH実装をandインターフェイスの背後に隠しIRepository<T>ます(したがって、理論的にはNHibernateをたとえばEntity Frameworkとかなり簡単に交換できます)。

Unityは依存性注入自体であるもの以外のコンストラクターパラメーターの受け渡しをサポートしていない(または少なくとも私が使用しているバージョンはサポートしていない)ため、既存のNHISessionを渡すことはできません。しかし、UOW内のすべてのオブジェクトが同じISessionを共有することを望んでいます。

スレッドごとにISessionへのアクセスを管理する制御リポジトリクラスを用意することで、これを解決します。

    public static ISession Session
    {
        get
        {
            lock (_lockObject)
            {
                // if a cached session exists, we'll use it
                if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                {
                    return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
                }
                else
                {
                    // must create a new session - note we're not caching the new session here... that's the job of
                    // BeginUnitOfWork().
                    return _factory.OpenSession(new NHibernateInterceptor());
                }
            }
        }
    }

この例では、Webコンテキストにない場合、またはWebコンテキストにある場合(スレッドプールの問題を回避するため)に格納されているにPersistenceFrameworkContext.Current.Itemsアクセスします。プロパティへの最初の呼び出しは、保存されたファクトリインスタンスからインスタンス化し、その後の呼び出しは、ストレージからそれを取得するだけです。ロックすると速度が少し遅くなりますが、appdomainスコープの静的インスタンスをロックするほどではありません。IList<object>ThreadStaticHttpContext.Current.ItemsISessionISession

次に、UOWを処理する方法がBeginUnitOfWorkありEndUnitOfWorkます。ネストされたUOWは、率直に言って管理が面倒だったため、特に許可していません。

    public void BeginUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                EndUnitOfWork();

            ISession session = Session;
            PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
        }
    }

    public void EndUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
            {
                ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
                PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
                session.Flush();
                session.Dispose();
            }
        }
    }

最後に、2つのメソッドがドメインタイプ固有のリポジ​​トリへのアクセスを提供します。

    public IRepository<T> For<T>()
        where T : PersistentObject<T>
    {
        return Container.Resolve<IRepository<T>>();
    }

    public TRepository For<T, TRepository>()
        where T : PersistentObject<T>
        where TRepository : IRepository<T>
    {
        return Container.Resolve<TRepository>();
    }

(ここでPersistentObject<T>は、IDとEqualsのサポートを提供する基本クラスです。)

したがって、特定のリポジトリへのアクセスはパターンになります

NHibernateRepository.For<MyDomainType>().Save();

次に、これをファサードして使用できるようにします

MyDomainType.Repository.Save();

特定のタイプに特殊なリポジトリがある場合(つまり、から取得できる以上のものが必要な場合) 、実装から継承する拡張実装であるIRepository<T>から派生するインターフェイスを作成し、ドメインタイプ自体で静的プロパティをオーバーライドします。IRepository<T>IRepository<T>Repositorynew

    new public static IUserRepository Repository
    {
        get
        {
            return MyApplication.Repository.For<User, IUserRepository>();
        }
    }

MyApplication[実際の製品ではあまりうなずくものと呼ばれます]は、RepositoryUnityを介してインスタンスを提供するファサードクラスであるため、ドメインクラス内の特定のNHibernateリポジトリ実装に依存しません。)

これにより、リポジトリの実装のためのUnityを介した完全なプラグイン可能性、フープを飛び越えることなくコードでリポジトリに簡単にアクセスできること、および透過的なスレッドごとのISession管理が可能になります。

上記のコードよりもはるかに多くのコードがあります(そして、サンプルコードを大幅に簡略化しました)が、一般的な考え方は理解できます。

MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble@wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically

私のWebアプリには、リクエストごとのセッションがあるのでBeginUnitOfWork、それぞれとEndUnitOfWorkで呼び出されます。BeginRequestEndRequest

于 2010-07-23T14:23:24.320 に答える
0

私はあなたと非常によく似た構造を持っています、そしてこれが私があなたの質問を解決する方法です:

1)各メソッドでコンテナを指定するために、静的プロパティを介して呼び出す個別のクラス( " SessionManager ")があります。そうすることで、Save実装を使用した例を次に示します。

private static ISession NHibernateSession
{
    get { return SessionManager.Instance.GetSession(); }
}

public T Save(T entity)
{
    using (var transaction = NHibernateSession.BeginTransaction())
    {
        ValidateEntityValues(entity);
        NHibernateSession.Save(entity);

        transaction.Commit();
    }

    return entity;
}

2)各ASPXページにコンテナが作成されていません。私は、global.asaxページでNHibernateの良さをすべてインスタンス化します。

**さらにいくつかのことが起こります**

3)ロードをインスタンス化するためにヘルパーは必要ありません。Loadの代わりにGetを使用することもできます。詳細情報@LoadとGetの違い

4)現在のコードを使用すると、必要なドメインオブジェクト(StoredWillRepository、PersonRepository、CategoryRepositoryなど)ごとにほぼ同じコードを繰り返す必要があります。これはドラッグのようです。次のように、ジェネリッククラスを使用してNHibernateを操作できます。

public class Dao<T> : IDao<T>
{
    public T SaveOrUpdate(T entity)
    {
        using (var transaction = NHibernateSession.BeginTransaction())
        {
            NHibernateSession.SaveOrUpdate(entity);
            transaction.Commit();
        }

        return entity;
    }
}

私の実装では、次のようなものを使用できます。

Service<StoredWill>.Instance.SaveOrUpdate(will);
于 2010-07-23T12:52:47.070 に答える
0

技術的には、私の質問に対する答えは、container.Resolveのオーバーロードを使用することです。これにより、コンストラクター引数を匿名型として指定できます。

IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });

しかし、それに直面しましょう。他のすべての人から提供された回答は、はるかに有益でした。

于 2010-07-23T16:03:10.400 に答える