7

Je travaille sur une application Asp.Net MVC 3 utilisant Fluent NHibernate. Je tente juste d'ajouter un conteneur IoC en utilisant StructureMap.StructureMap, NHibernate et plusieurs bases de données

J'ai implémenté une fabrique de contrôleurs personnalisée qui utilise StructureMap pour créer le contrôleur et injecter des dépendances. Chaque constructeur de contrôleur prend un ou plusieurs services, qui à leur tour prennent un DAO comme argument constructeur. Chaque constructeur DAO prend une ISessionFactory.

Pour mon registre StructureMap NHibernate Je donne les résultats suivants:

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

Tout cela fonctionne très bien pour une base de données unique et une usine de session. Cependant, l'application utilise plusieurs bases de données.

Quelle est la meilleure façon de gérer cela?

Répondre

9

L'enregistrement de plusieurs usines de session est facile - le problème est de choisir le bon quand vous en avez besoin. Par exemple, disons que nous avons une sorte de laboratoire qui a plusieurs bases de données. Chaque laboratoire a un emplacement et plusieurs échantillons pour cet emplacement. Nous pourrions avoir un SampleRepository qui modélise cela. Chaque emplacement a une clé unique pour l'identifier (par exemple "LabX", "LabY", "BlackMesa"). Nous pouvons utiliser cette clé unique comme nom de la chaîne de connexion à la base de données dans le fichier app.config. Dans cet exemple, nous aurions trois chaînes de connexion dans le fichier app.config. Voici une section échantillon 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> 

Ainsi, nous avons besoin d'une usine de session unique pour chaque chaîne de connexion. Créons un NamedSessionFactory qui enveloppe 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; 
    } 
} 

Maintenant, nous avons besoin de modifier votre AppSessionFactory un peu. Tout d'abord, ce que vous avez créé est une usine de fabrique de sessions - ce n'est pas tout à fait ce que nous recherchons. Nous voulons donner à notre usine un emplacement et en sortir une session, pas une usine de session. NHibernate fluide est ce qui nous donne des usines de session.

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

L'astuce ici est d'accepter une liste d'objets INamedSessionFactory dans le constructeur. StructureMap doit nous donner tous les objets INamedSessionFactory que nous avons enregistrés. Nous allons nous enregistrer dans une seconde.

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

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

C'est ici que la magie se produit. Étant donné une clé de localisation, nous parcourons notre liste d'usines en recherchant une qui porte le même nom que locationKey, puis nous lui demandons d'ouvrir une session et de la renvoyer à l'appelant.

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

     return sessionFactory.OpenSession(); 
    } 
} 

Maintenant, nous allons tout câbler ensemble.

internal class NHibernateRegistry : Registry 
{ 
    public NHibernateRegistry() 
    { 

Nous allons faire une boucle par toutes les chaînes de connexion dans notre fichier app.config (il y aurait trois d'entre eux dans cet exemple) et enregistrer un objet INamedSessionFactory pour chacun d'eux.

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

Nous devons également enregistrer IAppSessionFactory.

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

Vous remarquerez que nous avons déplacé cette logique hors de la classe d'usine ...Ce sont des méthodes auxiliaires pour créer des usines de sessions à partir 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>()); 
    } 
} 

Cela devrait le faire! Nous allons créer un dépôt pour obtenir nos échantillons ...

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

Maintenant vous pouvez obtenir une seule instance de SampleRepository et utiliser la méthode GetSamplesForLocation pour tirer des échantillons de l'une des trois bases de données que nous avons enregistrées dans app.config. Je pourrais vouloir éviter BlackMesa. Je comprends qu'il y avait des problèmes là-bas.

+0

Je constate sans doute que ça fait des années depuis que je l'ai utilisé StructureMap ou NHibernate - donc je pourrais avoir vissé quelque chose là-bas. Mais le modèle de base devrait être sain. J'espère que cela aide! –

+0

Une réponse vraiment utile. J'utilise actuellement TheInstanceNamed() de StrucutreMap pour affecter des SessionFactories aux DAO, ce qui semble fonctionner correctement. Je chercherai à mettre en œuvre vos suggestions lorsque j'aurai un peu de temps libre. Merci. – TonE

0

Êtes-vous sûr que cette chose fonctionne? chaîne ISessionFactory

public string ISessionFactory SessionFactory { get; private set; } 

devrait-il être

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; 
    } 
} 
Questions connexes