2012-05-26 12 views
18

Mam aplikację do testowania narzędzi. który używa płynnego mapowania nhibernate do obiektu db wewnątrz mssql db. Ponieważ chcę się uczyć dobrze dostroić nhib. aplikacje mvc3, używam tej aplikacji. do celów testowych, które mają tylko jedną prostą jednostkę z 10 właściwościami enum i jedną właściwością łańcucha znaków. Tak, to jest naprawdę lightwave, ale czas uruchamiania według profilera nhibernate wynosi 4,37 sec. Co jest naprawdę powolne w renderowaniu jednego obiektu z kilkoma zaznaczonymi/niezaznaczonymi właściwymi liniami.Optymalizowanie fabryki sesji nhibernate, czas uruchamiania aplikacji WebApp bardzo powolny

Kod jest następujący. Domain.SessionProvider.cs

public static ISessionFactory CreateSessionFactory() 
{ 
    var config = Fluently.Configure() 
      .Database(MsSqlConfiguration.MsSql2008 
      .ConnectionString(c => c.FromConnectionStringWithKey("myConnection"))) 
      .Mappings(m => m.FluentMappings.Add<FeaturesMap>()) 
      .ExposeConfiguration(p => p.SetProperty("current_session_context_class", "web")) 
      .BuildConfiguration(); 

      return config.BuildSessionFactory();    
} 

global.asax

public class MvcApplication : System.Web.HttpApplication 
{ 
    //SessionPerWebRequest is ommited here as well as other content 
    public static ISessionFactory SessionFactory = 
       SessionProvider.CreateSessionFactory(); 

    protected void Application_Start() 
    { 
     SessionFactory.OpenSession(); 
    } 
} 

Wewnątrz MyController Mam następujący:

public ActionResult Index() 
{ 
    return View(GetData()); 
} 

private IList<FeaturesViewModel> GetData() 
{ 
    List<Features> data; 
    using (ISession session = MvcApplication.SessionFactory.GetCurrentSession()) 
    { 
      using (ITransaction tx = session.BeginTransaction()) 
      { 
       data = session.Query<Features>().Take(5).ToList(); 
       tx.Commit(); 

       var viewModelData = FeaturesViewModel.FromDomainModel(data); 
       return viewModelData; 
      } 
     } 
} 
+1

Czy czas uruchomienia jest naprawdę tak ważny? Przy prawidłowym wdrożeniu koszt ten nie powinien często występować. – Lucero

+0

dotyczące aplikacji internetowych. Myślę, że ten czas startowy z wykorzystaniem tej encji lightwave to "coś". Mówisz, że sprawdzam wdrożenie? – BobRock

+0

Chodzi o to, że jeśli zdarza się to raz dziennie lub co kilka dni w środowisku wdrożonym (gdy pula aplikacji zostanie poddana recyklingowi), koszt ten nie jest decydujący. – Lucero

Odpowiedz

19

Możesz poprawić czas uruchamiania (zarówno aplikacji internetowych, jak i okien), buforując konfiguracje. Poniższa klasa zrobi to zadanie:

using System.IO; 
using System.Reflection; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Web; 
using NHibernate.Cfg; 

namespace NH32AutoMap.Core 
{ 
    public class ConfigurationFileCache 
    { 
     private readonly string _cacheFile; 
     private readonly Assembly _definitionsAssembly; 

     public ConfigurationFileCache(Assembly definitionsAssembly) 
     { 
      _definitionsAssembly = definitionsAssembly; 
      _cacheFile = "nh.cfg"; 
      if (HttpContext.Current != null) //for the web apps 
       _cacheFile = HttpContext.Current.Server.MapPath(
           string.Format("~/App_Data/{0}", _cacheFile) 
           ); 
     } 

     public void DeleteCacheFile() 
     { 
      if (File.Exists(_cacheFile)) 
       File.Delete(_cacheFile); 
     } 

     public bool IsConfigurationFileValid 
     { 
      get 
      { 
       if (!File.Exists(_cacheFile)) 
        return false; 
       var configInfo = new FileInfo(_cacheFile); 
       var asmInfo = new FileInfo(_definitionsAssembly.Location); 

       if (configInfo.Length < 5 * 1024) 
        return false; 

       return configInfo.LastWriteTime >= asmInfo.LastWriteTime; 
      } 
     } 

     public void SaveConfigurationToFile(Configuration configuration) 
     { 
      using (var file = File.Open(_cacheFile, FileMode.Create)) 
      { 
       var bf = new BinaryFormatter(); 
       bf.Serialize(file, configuration); 
      } 
     } 

     public Configuration LoadConfigurationFromFile() 
     { 
      if (!IsConfigurationFileValid) 
       return null; 

      using (var file = File.Open(_cacheFile, FileMode.Open, FileAccess.Read)) 
      { 
       var bf = new BinaryFormatter(); 
       return bf.Deserialize(file) as Configuration; 
      } 
     } 
    } 
} 

do korzystania z tego,

private Configuration readConfigFromCacheFileOrBuildIt() 
{ 
    Configuration nhConfigurationCache; 
    var nhCfgCache = new ConfigurationFileCache(MappingsAssembly); 
    var cachedCfg = nhCfgCache.LoadConfigurationFromFile(); 
    if (cachedCfg == null) 
    { 
     nhConfigurationCache = buildConfiguration(); 
     nhCfgCache.SaveConfigurationToFile(nhConfigurationCache); 
    } 
    else 
    { 
     nhConfigurationCache = cachedCfg; 
    } 
    return nhConfigurationCache; 
} 

a następnie przed wywołaniem BuildSessionFactory możemy odczytać plik konfiguracyjny z pamięci podręcznej lub jeśli mapowanie uległy zmianie, budować i ponownie buforuj:

public ISessionFactory SetUpSessionFactory() 
{ 
    var config = readConfigFromCacheFileOrBuildIt(); 
    var sessionFactory = config.BuildSessionFactory(); 

Tutaj można znaleźć pełną próbkę: (^). + Jeśli chcesz, aby działało, oddzielne klasy domen i zestawy definicji odwzorowań z zespołu głównej aplikacji (ponieważ klasa ConfigurationFileCache usunie plik pamięci podręcznej, jeśli zestaw definicji odwzorowań jest nowszy niż LastWriteTime pliku pamięci podręcznej).

+0

FYI: Próbowałem tego na 3.3 i to nie ma znaczenia. Może mój ssd jest szybszy niż starsze HD, ale problem z uruchomieniem pozostaje. – Candide

2

IIRC poprawnie, to nie jest dobry pomysł, aby tworzenie obiektów w contructor HttpApplication (lub jego podklas, takich jak MvcApplication) . Lepiej utworzyć fabrykę sesji w procedurze obsługi Application_Start.

Należy usunąć profiler NHibernate (ponieważ wszystkie profilery mogą wpływać na pomiary). Zamiast tego, wywołaj wywołanie CreateSessionFactory() i otaczaj je za pomocą klasy Stopwatch, aby uzyskać dokładny pomiar.

Czy twój serwer bazy danych nie odpowiada? Z powodu łączenia połączeń może to być zauważalne tylko przy pierwszej okazji.

NHibernate zajmuje trochę czasu, aby zainicjować, ale 4 sekundy z jednym lekkim podmiotem wydają się zbyt duże. Wpływ na to ma oczywiście ogólna wydajność systemu testowego.

+0

mając powyższy kod na uwadze, mogę poprosić o pokazanie mi, jak powinien wyglądać metoda Application_Start. Dzięki – BobRock

+0

W odniesieniu do oryginalnego kodu, po prostu przenieś wywołanie CreateSessionFactory() do Application_Start() i wywołanie OpenSession() do Application_BeginRequest(). Dodaj odpowiedni kod, aby zamknąć sesję w Application_EndRequest(). Użycie Begin | EndRequest odpowiada wzorowi sesja-na-żądanie. –