当我自己拿出一个解决方案时,我以为我会在那里得到这个问题。
在构建了大量应用程序之后,我有最后一分钟的要求来支持读/写一个额外的数据库(总共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如何以及何时指定适当的配置路径。新方法看起来像(大量借鉴了比利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;
}
答案 0 :(得分: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"
或{ {1}}给定实体的类型。
我们使用"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])));
将选择适当的会话,会话将继续被懒惰地初始化并由Autofac正确处理。
当然,您必须仔细考虑交易管理。
希望这可以解决问题! NB