8

自分で解決策を考えている間に、この質問を世に出そうと思いました。

アプリケーションの大部分を構築した後、追加のデータベースへの読み取り/書き込みをサポートするという土壇場の要件があります (合計 2 つ、他には知られていません)。DI/IoC コンポーネントを提供する Autofac を使用して、NHibernate を使用してアプリケーションを構築しました。FWIW、これは ASP.NET MVC 2 アプリにあります。

NHibernate セッションを使用する汎用リポジトリ クラスがあります。IRepository<>理論的には、2 番目のデータベースに渡されるセッションが適切な SessionFactory から生成される限り、この汎用リポジトリ ( ) を引き続き使用できますよね?

さて、アプリが起動すると、Autofac がそれを行います。Session と SessionFactory に関しては、次のようなモジュールがあります。

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession())
    .InstancePerMatchingLifetimeScope(WebLifetime.Request)
    .OnActivated(e =>
    {
        e.Context.Resolve<TransactionManager>().CurrentTransaction = ((ISession)e.Instance).BeginTransaction();
    });

 builder.Register(c => ConfigureNHibernate())
    .SingleInstance();

ここで、ベースの SessionFactory を返す ConfigureNHibernate() は次のようになります。

private ISessionFactory ConfigureNHibernate()
{
    Configuration cfg = new Configuration().Configure();
    cfg.AddAssembly(typeof(Entity).Assembly);
    return cfg.Configure().BuildSessionFactory();
}

現在、これは 1 つのデータベースのみに制限されています。他の NHib シナリオでは、個別の SessionFactories のインスタンスをハッシュに押し込み、必要に応じてそれらを取得する可能性があります。メジャー リリースがかなり近づいているので、全体を再構築する必要はありません。したがって、2 つの SessionFactory を個別に構成できるように、少なくとも上記のメソッドを変更する必要があると思います。私の灰色の領域は、特定のリポジトリ (または少なくともその 2 番目のデータベースに固有のエンティティ) で使用される正しい Factory を指定する方法です。

この方法で IoC コンテナーと NHibernate を使用しているときに、このシナリオの経験がある人はいますか?

編集 構成ファイルのパスを取得し、HttpRuntime.Cache に一致する SessionFactory が存在するかどうかを確認し、まだ存在しない場合は新しいインスタンスを作成し、その SessionFactory を返す GetSessionFactory メソッドをスタブ化しました。ここで、適切な構成パスを指定する方法とタイミングを Autofac に指示する方法を突き詰める必要があります。新しいメソッドは次のようになります(Billy の 2006 年の投稿から大幅に借用):

private ISessionFactory GetSessionFactory(string sessionFactoryConfigPath)
    {
        Configuration cfg = null;
        var sessionFactory = (ISessionFactory)HttpRuntime.Cache.Get(sessionFactoryConfigPath);

        if (sessionFactory == null)
        {
            if (!File.Exists(sessionFactoryConfigPath))
                throw new FileNotFoundException("The nhibernate configuration file at '" + sessionFactoryConfigPath + "' could not be found.");

            cfg = new Configuration().Configure(sessionFactoryConfigPath);
            sessionFactory = cfg.BuildSessionFactory();

            if (sessionFactory == null)
            {
                throw new Exception("cfg.BuildSessionFactory() returned null.");
            }

            HttpRuntime.Cache.Add(sessionFactoryConfigPath, sessionFactory, null, DateTime.Now.AddDays(7), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null);
        }

        return sessionFactory;
    }
4

1 に答える 1

11

さまざまな種類のエンティティを各データベースに入れたいと想定しています。各データベースに同じ種類のエンティティを保持したい場合は、AutofacContrib.Multitenant を調べてください。

このシナリオに役立つ 2 つの要素は次のとおりです。

最初に、名前付きサービスを使用して 2 つの異なるデータベースを参照します。私はそれら"db1"を呼び出します"db2. "。セッションに至るまで、データベースに関連するすべてのコンポーネントは、名前で登録されます:

builder.Register(c => ConfigureDb1())
    .Named<ISessionFactory>("db1")
    .SingleInstance();

builder.Register(c => c.ResolveNamed<ISessionFactory>("db1").OpenSession())
    .Named<ISession>("db1")
    .InstancePerLifetimeScope();

// Same for "db2" and so-on.

ここで、コンストラクター パラメーターとしてNHibernateRepository<T>を受け入れる型があり、エンティティの型が指定されたときにまたはを返すISession関数を記述できるとします。WhichDatabase(Type entityType)"db1""db2"

を使用しResolvedParameterて、エンティティ タイプに基づいてセッションを動的に選択します。

builder.RegisterGeneric(typeof(NHibernateRepository<>))
    .As(typeof(IRepository<>))
    .WithParameter(new ResolvedParameter(
        (pi, c) => pi.ParameterType == typeof(ISession),
        (pi, c) => c.ResolveNamed<ISession>(
            WhichDatabase(pi.Member.DeclaringType.GetGenericArguments()[0])));

(警告 - Google Chrome でコンパイルおよびテスト済み;))

これで、解決IRepository<MyEntity>によって適切なセッションが選択され、セッションは Autofac によって引き続き遅延初期化され、正しく破棄されます。

もちろん、トランザクション管理については慎重に検討する必要があります。

これでうまくいくことを願っています!注意

于 2010-12-15T21:39:16.120 に答える