2010-12-14 34 views
8

多个数据库,我想我会得到这个问题在那里,而我noodled对我自己的解决方案。管理与NHibernate和Autofac

具有内置了大量的应用程序后,我有一个最后一分钟的要求,以支持读取/写入到一个附加的数据库(2总,没有已知的其他)。我使用NHibernate构建应用程序,Autofac提供DI/IoC组件。 FWIW,它位于ASP.NET MVC 2应用程序中。

我有一个通用的存储库类,带有一个NHibernate的会议。理论上,只要传递给它的会话是从适当的SessionFactory产生的,我可以继续为第二个数据库使用此通用存储库(IRepository<>),对吧?

好了,应用程序启动时,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(); 

其中ConfigureNHibernate(),它返回基地SessionFactory的,看起来像:

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

目前,这仅限于刚刚一个数据库。在任何其他NHib场景中,我可能会将单独的SessionFactory的实例推送到散列中,并根据需要检索它们。我不想重新设计整个事物,因为我们离主要版本相当近。所以,我猜我至少需要修改上面的方法,以便我可以独立配置两个SessionFactory。我的灰色地带是我将如何指定正确的Factory与特定存储库一起使用(或至少对于特定于第二个数据库的实体)。

任何人都在使用这种方式的IoC容器和NHibernate有这种情况的经验吗?

编辑 我掐灭,需要一个配置文件路径的方法的getSessionFactory,在HttpRuntime.Cache匹配的SessionFactory是否存在等检查,创建一个新的实例,如果一个已经不存在,和返回SessionFactory。现在我仍然需要了解如何告诉Autofac如何以及何时指定合适的配置路径。新方法看起来像(比利2006年后here大量借款):

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; 
    } 
+1

将会话工厂存储在缓存中是一个坏主意。这不是可以消失并被重新创造的东西。 – 2010-12-14 22:51:48

+0

我知道。实际上,我将不得不管理该缓存对象的生命周期。比利的例子实际上更进一步,创建了一个单例范围的SessionManager类来处理缓存问题。我试图贬低它,所以我最终可以让Autofac通过它自己的(非常优雅的)范围机制来管理它。 – nkirkes 2010-12-14 23:11:09

回答

11

我假设你想在不同种类的实体进入每个数据库的;如果您想在每个数据库中保留相同类型的实体,请查看AutofacContrib.Multitenant。

两种成分可与这种情况下帮助是:在这一个

首先,使用命名服务,指的是两个不同的数据库。我会称它们为"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]))); 

(警告 - 编译和谷歌浏览器测试;))

现在,解决IRepository<MyEntity>将选择适当的会话和会话将继续懒洋洋地初始化和Autofac正确配置。当然,你将不得不仔细考虑交易管理。

希望这个窍门! NB

+1

伙计,就是这样。好棒!我为我的场景做了一些调整,并且更深入地挖掘了更新的Autofac源代码,以便了解发生了什么,但是,是的,这就是我喜欢的原因!谢谢尼古拉斯! – nkirkes 2010-12-15 23:47:39

+0

非常感谢 - 不客气! – 2010-12-16 01:43:33