2010-12-14 2 views
8

Je pensais que j'aurais cette question là-bas pendant que je noodled sur une solution par moi-même. Après avoir construit la majeure partie d'une application, j'ai une exigence de dernière minute pour prendre en charge la lecture/écriture dans une base de données supplémentaire (2 au total, pas d'autres connus). J'ai construit l'application en utilisant NHibernate, avec Autofac fournissant les composants DI/IoC. FWIW, cela réside dans une application ASP.NET MVC 2.Gestion de plusieurs bases de données avec NHibernate et Autofac

J'ai une classe de référentiel générique qui prend une session NHibernate. Théoriquement, je peux continuer à utiliser ce référentiel générique (IRepository<>) pour la deuxième base de données tant que la session qui lui est passée est créée à partir d'une SessionFactory appropriée, n'est-ce pas?

Eh bien, lorsque l'application démarre, Autofac le fait. En ce qui concerne la session et SessionFactory, j'ai un module qui déclare:

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

où ConfigureNHibernate(), qui retourne la SessionFactory de base, ressemble à:

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

Actuellement, cela est limité à seulement la base de données unique. Dans n'importe quel autre scénario de NHib, je placerais probablement des instances de SessionFactories séparées dans un hachage, et les récupèrerais au besoin. Je ne veux pas avoir à réorganiser le tout car nous sommes assez proches d'une version majeure. Donc, je devine que j'ai besoin de modifier au moins les méthodes ci-dessus afin que je puisse configurer indépendamment deux SessionFactories. Ma zone grise est la façon dont je vais spécifier la bonne Usine à utiliser avec un référentiel spécifique (ou au moins pour les entités spécifiques à cette deuxième base de données).

Quelqu'un at-il l'expérience de ce scénario lors de l'utilisation d'un conteneur IoC et de NHibernate de cette manière?

EDIT J'ai écrasa une méthode GetSessionFactory qui prend un chemin de fichier de configuration, vérifie l'existence d'un SessionFactory correspondant dans la HttpRuntime.Cache, crée si l'on n'existe pas déjà une nouvelle instance, et renvoie cette SessionFactory. Maintenant, j'ai encore besoin de savoir comment dire à Autofac comment et quand spécifier un chemin de configuration approprié. La nouvelle méthode ressemble à (beaucoup emprunté après here de Billy 2006):

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

Le stockage de la fabrique de sessions dans le cache est une mauvaise idée. Ce n'est pas quelque chose qui peut simplement disparaître et être recréé. –

+0

Je sais. En réalité, je vais devoir gérer la durée de vie de cet objet mis en cache. L'exemple de Billy va même plus loin et crée une classe SessionManager portée par un singleton qui prend en charge les problèmes de mise en cache. J'essayais de le berner pour que Autofac puisse le gérer via ses propres mécanismes (très élégants). – nkirkes

Répondre

11

Je suppose que vous voulez différents types d'entités à entrer dans chaque base de données; Si vous souhaitez conserver les mêmes types d'entités dans chaque base de données, consultez AutofacContrib.Multitenant.

Les deux ingrédients qui peuvent aider à ce scénario sont:

D'abord, utilisez les services nommés pour faire référence aux deux bases de données différentes. Je les appellerai "db1" et "db2 ".Tous les composants relatifs à la base de données, tout le chemin jusqu'à la session, se faire enregistrer avec un nom:

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. 

Maintenant, en supposant que vous avez un type NHibernateRepository<T> qui accepte un ISession comme paramètre constructeur, et que vous peut écrire une fonction WhichDatabase(Type entityType) qui renvoie soit "db1" soit "db2" lorsqu'elle reçoit le type d'entité.

Nous utilisons un ResolvedParameter pour choisir dynamiquement la session en fonction du type d'entité.

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

(Attention - compilé et testé dans Google Chrome;))

Maintenant, la résolution IRepository<MyEntity> sélectionnera la session appropriée, et les sessions continueront d'être paresseusement initialisées et disposés correctement Autofac.

Bien sûr, vous devrez bien réfléchir à la gestion des transactions.

Espérons que cela fait l'affaire! NB

+1

Mec, c'était ça. Génial! J'ai eu quelques ajustements à faire pour mon scénario, et j'ai creusé un peu plus dans la source Autofac mise à jour pour que je comprenne ce qui se passait, mais bon dieu oui, c'est pourquoi j'aime SO! Merci Nicolas! – nkirkes

+0

Super à entendre - de rien! –

Questions connexes