在构建了大量应用程序之后,我有最后一分钟的要求来支持读/写一个额外的数据库(总共2个,没有已知的其他数据库).我使用NHibernate构建了应用程序,Autofac提供了DI / IoC组件. FWIW,它驻留在ASP.NET MVC 2应用程序中.
我有一个通用的存储库类,它采用NHibernate会话.从理论上讲,我可以继续将这个通用存储库(IRepository<>)用于第二个数据库,只要传递给它的会话是从适当的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(); }
目前,这仅限于一个数据库.在任何其他NHib场景中,我可能会将单独的SessionFactories的实例推送到哈希中,并根据需要检索它们.我不想重新设计整个事情,因为我们非常接近主要版本.所以,我猜我至少需要修改上面的方法,以便我可以独立配置两个SessionFactories.我的灰色区域是我将如何指定正确的工厂与特定存储库(或至少对于第二个数据库特定的实体).
在以这种方式使用IoC容器和NHibernate时,任何人都有使用此方案的经验吗?
编辑
我已经删除了一个GetSessionFactory方法,该方法接受配置文件路径,检查HttpRuntime.Cache中匹配的SessionFactory的存在,创建一个新实例(如果尚不存在),并返回该SessionFactory.现在我仍然需要告诉如何告诉Autofac如何以及何时指定适当的配置路径.新方法看起来像(从Billy 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; }
解决方法
可以帮助解决这种情况的两个因素是:
>命名服务http://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices
>已解决的参数http://code.google.com/p/autofac/wiki/ResolveParameters(关于这个的最小文档 – 即将发布的Autofac 2.4版本有一些语法甜味剂……)
首先,使用命名服务来引用两个不同的数据库.我将它们称为“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