2010-12-14 27 views
8

Pensé que podría obtener esta pregunta mientras redactaba una solución por mi cuenta.Gestionando múltiples bases de datos con NHibernate y Autofac

Después de haber desarrollado la mayor parte de una aplicación, tengo un requisito de último minuto para admitir la lectura/escritura en una base de datos adicional (2 en total, sin conocer otras). Creé la aplicación usando NHibernate, con Autofac suministrando los componentes DI/IoC. FWIW, esto reside en una aplicación ASP.NET MVC 2.

Tengo una clase de repositorio genérico que toma una sesión de NHibernate. Teóricamente, puedo continuar usando este repositorio genérico (IRepository<>) para la segunda base de datos siempre que la sesión que se pasa a ella se genere desde una SessionFactory apropiada, ¿verdad?

Bueno, cuando se inicia la aplicación, Autofac lo hace. En cuanto a la sesión y SessionFactory, tengo un módulo que establece:

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(); 

donde ConfigureNHibernate(), que devuelve el SessionFactory base, se ve así:

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

Actualmente, esto está limitado a solo la única base de datos. En cualquier otro escenario de NHib, probablemente empujaría las instancias de SessionFactories por separado a un hash, y las recuperaría según fuera necesario. No quiero tener que volver a diseñar todo ya que estamos bastante cerca de un lanzamiento importante. Entonces, supongo que necesito modificar al menos los métodos anteriores para poder configurar de forma independiente dos SessionFactories. Mi área gris es cómo voy a especificar la fábrica correcta que se utilizará con un repositorio específico (o al menos para las entidades específicas de esa segunda base de datos).

¿Alguien tiene experiencia con este escenario mientras usa un contenedor de IoC y NHibernate de esta manera?

EDITAR He apagó un método GetSessionFactory que toma una ruta de archivo de configuración, comprueba la existencia de una SessionFactory coincidente en la HttpRuntime.Cache, crea una nueva instancia si uno no existe ya, y devuelve esa SessionFactory. Ahora todavía necesito explicar cómo decirle a Autofac cómo y cuándo especificar una ruta de configuración adecuada. El nuevo método se parece a (prestado en gran medida de la posterior a 2006 de Billy 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

Almacenar la fábrica de la sesión en el caché es una mala idea. No es algo que pueda desaparecer y ser recreado. –

+0

Lo sé. En realidad, voy a tener que administrar la vida útil de ese objeto en caché. El ejemplo de Billy en realidad va un paso más allá y creó un producto único con ámbito de clase SessionManager que se ocupa de las preocupaciones de almacenamiento en caché. Yo estaba tratando de embrutecer por lo que podría finalmente dejar que Autofac administrarlo a través de él es poseer (muy elegante) mecanismos de ámbito. – nkirkes

Respuesta

11

Estoy asumiendo que desea diferentes tipos de entidades para entrar en cada base de datos; si desea mantener el mismo tipo de entidades en cada base de datos, consulte AutofacContrib.Multitenant.

Los dos ingredientes que pueden ayudar con este escenario son:

En primer lugar, use los servicios con nombre para hacer referencia a las dos bases de datos diferentes. Los llamaré "db1" y "db2 ".Todos los componentes relacionados con la base de datos, todo el camino hasta la sesión, registrarse con un nombre:

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. 

Ahora, suponiendo que tiene un tipo NHibernateRepository<T> que acepta un ISession como su parámetro constructor, y que se puede escribir una función WhichDatabase(Type entityType) que devuelva "db1" o "db2" cuando se le dé el tipo de entidad.

se utiliza un ResolvedParameter elegir dinámicamente la sesión basado en el tipo de entidad.

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]))); 

(Advertencia - compilado y probado en Google Chrome;))

Ahora, la resolución de IRepository<MyEntity> se seleccione la sesión apropiada, y las sesiones continuarán siendo perezosamente inicializado correctamente y dispuesto por Autofac.

Por supuesto, tendrá que pensar cuidadosamente en la gestión de transacciones.

Espero que esto haga el truco! NB

+1

Amigo, eso fue todo. ¡Tan genial! Tenía algunos ajustes que hacer para mi escenario, y excavado en un poco más en la fuente de Autofac actualizada de forma entendía lo que estaba pasando, pero claro que sí, es por eso que amo! Gracias Nicholas! – nkirkes

+0

¡Genial para escuchar, de nada! –

Cuestiones relacionadas