Question

Je pensais que je rentrais à cette question là-bas alors que je noodled une solution moi-même.

Après avoir construit la majeure partie d'une application, j'ai une exigence de dernière minute pour le soutien de lecture / écriture à une base de données supplémentaire (2 au total, d'autres pas connus). Je construit l'application en utilisant NHibernate, avec Autofac l'alimentation des composants DI / IoC. FWIW, ce réside dans une application ASP.NET MVC 2.

J'ai une classe générique référentiel 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 est transmis à elle a donné naissance à partir d'un SessionFactory approprié, non?

Eh bien, lorsque l'application démarre, Autofac ne chose est tout. En ce qui concerne la session et SessionFactory, j'ai un module états:

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

À l'heure actuelle, cela est limitée à une base de données. Dans tout autre scénario NHib, j'avais des instances Shove probables des SessionFactories séparés dans un hachage, et les récupérer au besoin. Je ne veux pas avoir à re-architecte tout ça que nous sommes assez près d'une version majeure. Donc, je suppose que je dois modifier au moins les méthodes ci-dessus pour que je puisse configure indépendamment deux SessionFactories. Ma zone grise est de savoir comment je vais sur la spécification du usine où être utilisé avec un référentiel spécifique (ou tout au moins pour les entités spécifiques à cette seconde base de données).

Toute personne ont de l'expérience avec ce scénario tout en utilisant un conteneur IoC et 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 une nouvelle instance si l'on n'existe déjà, et les rapports que SessionFactory. Maintenant, je dois encore marteler comment dire Autofac comment et quand spécifier un chemin de configuration appropriée. La nouvelle méthode ressemble comme (beaucoup emprunté du poste 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;
    }
Était-ce utile?

La solution

Je suppose que vous voulez différents types d'entités à entrer dans chaque base de données; si vous voulez garder 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:

Tout d'abord, l'utilisation des services nommé pour faire référence aux deux bases de données différentes. Je vais les appeler "db1" et "db2 » Tous les composants liés à 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, supposant que vous avez un NHibernateRepository<T> de type qui accepte un ISession comme paramètre constructeur, et que vous pouvez écrire un WhichDatabase(Type entityType) de fonction qui retourne soit "db1" ou "db2" quand étant donné le type d'une entité.

Nous utilisons un ResolvedParameter 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])));

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

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

Vous devrez réfléchir à la gestion des transactions bien sûr.

Espérons que cela fait l'affaire! NB

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top