2010-12-14 5 views
8

Pomyślałem, że dostanę to pytanie, podczas gdy ja osobiście przygotowałem rozwiązanie.Zarządzanie wieloma bazami danych za pomocą NHibernate i Autofac

Po zbudowaniu większości aplikacji, mam w ostatniej chwili wymóg obsługi odczytu/zapisu do dodatkowej bazy danych (2 całkowite, nieznane inne). Zbudowałem aplikację przy pomocy NHibernate, a Autofac dostarcza komponenty DI/IoC. FWIW, to rezyduje w aplikacji ASP.NET MVC 2.

Mam ogólne klasy repozytorium, które ma sesję NHibernate. Teoretycznie mogę nadal używać tego ogólnego repozytorium (IRepository<>) dla drugiej bazy danych, o ile sesja, która zostanie mu przekazana, zostanie utworzona z odpowiedniego SessionFactory, prawda?

Po uruchomieniu aplikacji Autofac robi to. W odniesieniu do sesji i SessionFactory, mam moduł, który stwierdza:

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

gdzie ConfigureNHibernate(), która zwraca SessionFactory bazową, wygląda następująco:

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

Obecnie ta jest ograniczona do zaledwie jedna baza danych. W każdym innym scenariuszu nibib, prawdopodobnie wepchnęłbym instancje oddzielnych SessionFactories w skrót, i odzyskałbym je w razie potrzeby. Nie chcę ponownie projektować całości, ponieważ jesteśmy blisko wydania głównego. Zgaduję, że muszę zmodyfikować przynajmniej powyższe metody, aby móc samodzielnie skonfigurować dwa SessionFactories. Mój szary obszar to sposób, w jaki określę poprawną fabrykę używaną z określonym repozytorium (lub przynajmniej dla jednostek właściwych dla tej drugiej bazy danych).

Ktoś ma doświadczenie w tym scenariuszu podczas korzystania z kontenera IoC i NHibernate w ten sposób?

EDIT mam zgaszone się GetSessionFactory metodę, która pobiera ścieżkę pliku konfiguracyjnego, czeki do istnienia z SessionFactory dopasowującego w HttpRuntime.Cache, tworzy nową instancję jeśli nie istnieje, a zwraca wartość SessionFactory. Teraz wciąż muszę zastanowić się, jak powiedzieć Autofac, jak i kiedy określić odpowiednią ścieżkę konfiguracji. Nowa metoda wygląda (pożyczył ciężko od Billy'ego 2006 postu 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

Przechowywanie fabryki sesji w pamięci podręcznej to zły pomysł. To nie jest coś, co może zniknąć i zostać odtworzone. –

+0

Wiem. W rzeczywistości będę musiał zarządzać czasem życia tego buforowanego obiektu. Przykład Billy'ego czyni to o krok dalej i tworzy singletonową klasę SessionManager, która zajmuje się problemami z buforowaniem. Próbowałem go zgubić, aby w końcu pozwolić Autofacowi zarządzać nim poprzez własne (bardzo eleganckie) mechanizmy scopingowe. – nkirkes

Odpowiedz

11

Jestem zakładając, że chcesz różnych rodzajów jednostek, aby przejść do każdej bazy danych; jeśli chcesz zachować te same rodzaje jednostek w każdej bazie danych, sprawdź AutofacContrib.Multitenant.

Dwa składniki, które mogą pomóc w tej sytuacji są:

Najpierw użyj nazwanych usług, aby odnieść się do dwóch różnych baz danych. Nazwiemy je "db1" i "db2 ".Wszystkich elementów odnoszących się do bazy danych, aż do sesji, dostać zarejestrowana nazwa:

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. 

Teraz, zakładając, że masz typ NHibernateRepository<T> która akceptuje ISession jako parametr konstruktora, i że może napisać funkcję WhichDatabase(Type entityType), która zwraca "db1" lub "db2", gdy zostanie podany typ obiektu.

Używamy ResolvedParameter do dynamicznego wybierania sesji na podstawie typu jednostki.

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

(Ostrzeżenie - opracowany i przetestowany w Google Chrome;))

Teraz rozwiązywania IRepository<MyEntity> wybierze odpowiednią sesję i sesje będą nadal leniwie zainicjowane i prawidłowo umieszczony przez Autofac.

Oczywiście będziesz musiał dokładnie przemyśleć zarządzanie transakcjami.

Mam nadzieję, że to wystarczy! NB

+1

Stary, to było to. Niesamowite! Miałem trochę do zrobienia w moim scenariuszu, i zagłębiłem się trochę w zaktualizowanym źródle Autofac, więc zrozumiałem, co się dzieje, ale piekło tak, właśnie dlatego kocham TAK! Dzięki Nicholas! – nkirkes

+0

Świetne do słuchania - nie ma za co! –