7

Estoy trabajando en una aplicación Asp.Net MVC 3 utilizando Fluent NHibernate. Solo intento agregar un contenedor IoC usando StructureMap.StructureMap, NHibernate y bases de datos múltiples

He implementado una fábrica de controlador personalizado que usa StructureMap para crear el controlador e inyectar dependencias. Cada constructor de controlador toma uno o más servicios, que a su vez toman un DAO como argumento de constructor. Cada constructor de DAO toma una ISessionFactory.

Para mi registro StructureMap NHibernate Tengo el siguiente:

internal class NHibernateRegistry : Registry 
{ 
    public NHibernateRegistry() 
    { 
     var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString; 

     For<ISessionFactory>() 
       .Singleton() 
       .Use(x => new AppSessionFactory().GetSessionFactory(connectionString)); 

     For<ISession>() 
      .HybridHttpOrThreadLocalScoped() 
      .Use(x => x.GetInstance<ISessionFactory>().OpenSession()); 
    } 

} 

public class AppSessionFactory 
{ 
    public ISessionFactory GetSessionFactory(string connectionString) 
    { 
     return GetConfig(connectionString) 
       .BuildSessionFactory(); 
    } 

    public static FluentConfiguration GetConfig(string connectionString) 
    { 
     return Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString))) 
      .Mappings(
       x => x.FluentMappings.AddFromAssemblyOf<AppEntity>()); 
    } 
} 

Todo esto funciona bien para una base de datos única y sola fábrica sesión. Sin embargo, la aplicación usa múltiples bases de datos.

¿Cuál es la mejor manera de manejar esto?

Respuesta

9

Registrar varias fábricas de sesión es fácil: el problema es seleccionar la correcta cuando la necesite. Por ejemplo, digamos que tenemos algún tipo de laboratorio que tiene múltiples bases de datos. Cada laboratorio tiene una ubicación y varias muestras para esa ubicación. Podríamos tener un SampleRepository que lo modele. Cada ubicación tiene una clave única para identificarlo (por ejemplo, "LabX", "LabY", "BlackMesa"). Podemos usar esa clave única como el nombre de la cadena de conexión de la base de datos en el archivo app.config. En este ejemplo, tendríamos tres cadenas de conexión en el archivo app.config. A continuación se muestra una sección connectionStrings:

<connectionStrings> 
    <add name="LabX" connectionString="Data Source=labx;User ID=someuser;Password=somepassword"/> 
    <add name="LabY" connectionString="Data Source=laby;User ID=someuser;Password=somepassword"/> 
    <add name="BlackMesa" connectionString="Data Source=blackmesa;User ID=freemang;Password=crowbar"/> 
</connectionStrings> 

Por lo tanto, tenemos que tener una fábrica de sesión único para cada cadena de conexión. Vamos a crear un NamedSessionFactory que envuelve ISessionFactory:

public interface INamedSessionFactory 
{ 
    public string Name { get; } // The name from the config file (e.g. "BlackMesa") 
    public ISessionFactory SessionFactory { get; } 
} 

public class NamedSessionFactory : INamedSessionFactory 
{ 
    public string Name { get; private set; } 
    public ISessionFactory SessionFactory { get; private set; } 

    public NamedSessionFactory(string name, ISessionFactory sessionFactory) 
    { 
     Name = name; 
     SessionFactory = sessionFactory; 
    } 
} 

Ahora tenemos que modificar un poco AppSessionFactory. En primer lugar, lo que ha creado es una fábrica de fábrica de sesiones; eso no es exactamente lo que estamos buscando. Queremos darle a nuestra fábrica una ubicación y obtener una sesión de ella, no una fábrica de sesiones. Fluidez NHibernate es lo que nos da fábricas de sesión.

public interface IAppSessionFactory 
{ 
    ISession GetSessionForLocation(string locationKey); 
} 

El truco aquí es aceptar una lista de objetos INamedSessionFactory en el constructor. StructureMap debería darnos todos los objetos INamedSessionFactory que hemos registrado. Nos registraremos en un segundo.

public class AppSessionFactory : IAppSessionFactory 
{ 
    private readonly IList<INamedSessionFactory> _factories; 

    public AppSessionFactory(IEnumerable<INamedSessionFactory factories) 
    { 
     _factories = new List<INamedSessionFactory>(factories); 
    } 

Aquí es donde ocurre la magia. Dada una clave de ubicación, revisamos nuestra lista de fábricas buscando una con el mismo nombre que locationKey, luego solicitamos que abra una sesión y se la devuelva a la persona que llama.

public ISession GetSessionForLocation(string locationKey) 
    { 
     var sessionFactory = _factories.Where(x => x.Name == locationKey).Single(); 

     return sessionFactory.OpenSession(); 
    } 
} 

Ahora conectemos todo esto.

internal class NHibernateRegistry : Registry 
{ 
    public NHibernateRegistry() 
    { 

Vamos a recorrer todos las cadenas de conexión en nuestro archivo app.config (que habría tres de ellos en este ejemplo) y registrar un objeto INamedSessionFactory para cada uno.

 foreach (ConnectionStringSettings location in ConfigurationManager.ConnectionStrings) 
     { 
      For<INamedSessionFactory>() 
       .Singleton() 
       .Use(x => new NamedSessionFactory(
        location.Name, 
        GetSessionFactory(location.ConnectionString)); 
     } 

También necesitamos registrar IAppSessionFactory.

 For<IAppSessionFactory>() 
      .Singleton() 
      .Use<AppSessionFactory>(); 
    } 

Notarás que hemos sacado esta lógica de la clase de fábrica ...Estos son métodos auxiliares para crear fábricas de sesiones de Fluent NHibernate.

private static ISessionFactory GetSessionFactory(string connectionString) 
    { 
     return GetConfig(connectionString) 
       .BuildSessionFactory(); 
    } 

    public static FluentConfiguration GetConfig(string connectionString) 
    { 
     return Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString))) 
      .Mappings(
       x => x.FluentMappings.AddFromAssemblyOf<AppEntity>()); 
    } 
} 

Eso debería hacerlo! Vamos a crear un repositorio para conseguir en nuestras muestras ...

public class SampleRepository 
{ 
    private readonly IAppSessionFactory _factory; 

    public SampleRepository(IAppSessionFactory factory) 
    { 
     _factory = factory; 
    } 

    public IEnumerable<Sample> GetSamplesForLocation(Location location) 
    { 
     using (ISession session = _factory.GetSessionForLocation(location.Key) 
     { 
      foreach (Sample sample in session.Query<Sample>()) 
       yield return sample; 
     } 
    } 
} 

Ahora usted puede conseguir una sola instancia de SampleRepository y utilizar el método GetSamplesForLocation para tirar de las muestras de cualquiera de las tres bases de datos que hemos registrado en app.config. Sin embargo, podría querer evitar BlackMesa. Entiendo que hubo problemas allí.

+0

probablemente debería tener en cuenta que han pasado años desde que he usado StructureMap o NHibernate - por lo que podría haber atornillado algo allá arriba. Pero el patrón básico debe ser bueno. ¡Espero eso ayude! –

+0

Una respuesta realmente útil. Actualmente estoy usando TheInstanceNamed() de StrucutreMap para asignar SessionFactories a DAO, lo que parece funcionar bien. Consideraré la implementación de sus sugerencias cuando tenga un poco de tiempo libre. Gracias. – TonE

0

¿Estás seguro de que esto funciona? cadena ISessionFactory

public string ISessionFactory SessionFactory { get; private set; } 

debe ser esto

public interface INamedSessionFactory 
{ 
    ISessionFactory SessionFactory { get; set; } 
    string Name { get; } 
} 

public class NamedSessionFactory : INamedSessionFactory 
{ 
    public ISessionFactory SessionFactory { get; set; } 
    public string Name { get; private set; } 

    public NamedSessionFactory(string Name, ISessionFactory SessionFactory) 
    { 
     this.Name = Name; 
     this.SessionFactory = SessionFactory; 
    } 
} 
Cuestiones relacionadas